You can see some of the cooler effects in this one.
You can see some of the cooler effects in this one.
I code a fair bit of nice organic colour fades for LED driving programs in Arduino, so decided to create a simple bit of code I could call in these situations. When the startTween(parametersHere) function is called, the specified LED will fade from the state it is currently, to the newly specified colour, and brightness.
Specifiable parameters include:
It handles fade interruptions too, so you can call a fade, and then call another one before the first is finished and it will run smoothly from the colour at that point in the fade, to the destination colour.
I’ll most likely convert it to a library when I get a chance. It could most likely be modified to work with any digitally-addressable LEDs. The code probably isn’t the MOST efficient way of doing things, but it works for me.
The original LPD8808 sketch was written by Adafruit. You’ll need the LPD8806 library from them to run this sketch.
#include "LPD8806.h"
#include "SPI.h"
// Example to control LPD8806-based RGB LED Modules in a strip
/*****************************************************************************/
// Number of RGB LEDs in strand:
#define nLEDsForArrays 4 //change this to how many LEDs you're using
int nLEDs = 4; //change this to how many LEDs you're using
// Chose 2 pins for output; can be any valid output pins:
int dataPin = 2;
int clockPin = 3;
int currentRGB[3] = {0,0,0};//this array is a temporary colour value store used by the 'updateLEDs' function
byte colourArray[13][3] = { //13 colours stored in an array
{126,0,0}, //0 RED
{126,126,0}, //1 ORANGE
{0,126,126}, //2 YELLOW
{63,90,0}, //3 CHARTREUSE GREEN
{0,126,0}, //4 ELECTRIC GREEN
{0,126,63}, //5 SPRING GREEN
{0,126,126}, //6 CYAN
{0,63,126}, //7 AZURE
{0,0,126}, //8 BLUE
{63,0,126}, //9 VIOLET
{126,0,126}, //10 MAGENTA
{126,0,63}, //11 ROSE
{126,126,126} //12 WHITE
};
int ledInfo[nLEDsForArrays][11] = {
{0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0}
};
//This array stores all the information for each LED. What exactly is noted below along with it's place in the array:
//0 = current time
//1 = duration of tween
//2 = previous red value
//3 = previous blue value
//4 = previous green value
//5 = new red value
//6 = new blue value
//7 = new green value
//8 = old brightness
//9 = new brightness
//10 = tween in progress // 0 = no tween in progress. 1 = tween in progress
//Correction table for perceived light intensity by human eye.
//These values are pretty much derived from the equation below:
//for(int i=0; i<126; i++) {
// x = round( pow( 2.0, i/18.0) - 1);
//}
//PROGMEM stores this array in the non-volatile flash memory so that it doesn't take up valuble RAM.
//When reading back PROGMEM values, it has to be done like this: pgm_read_byte(&(itemInTheArray))
byte humanColour[]PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6,
6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20,
21, 22, 23, 23, 24, 25, 26, 28, 29, 30, 31, 32, 34, 35, 36, 38,
39, 41, 43, 44, 46, 48, 50, 52, 54, 56, 58, 61, 63, 66, 68, 71,
74, 77, 80, 83, 86, 90, 93, 97, 101, 105, 109, 113, 118, 122, 126
};
// First parameter is the number of LEDs in the strand. The LED strips
// are 32 LEDs per meter but you can extend or cut the strip. Next two
// parameters are SPI data and clock pins:
//LPD8806 strip = LPD8806(32, dataPin, clockPin);
// You can optionally use hardware SPI for faster writes, just leave out
// the data and clock pin parameters. But this does limit use to very
// specific pins on the Arduino. For "classic" Arduinos (Uno, Duemilanove,
// etc.), data = pin 11, clock = pin 13. For Arduino Mega, data = pin 51,
// clock = pin 52. For 32u4 Breakout Board+ and Teensy, data = pin B2,
// clock = pin B1. For Leonardo, this can ONLY be done on the ICSP pins.
LPD8806 strip = LPD8806(nLEDs);
void setup() {
// Start up the LED strip
strip.begin();
// Update the strip, to start they are all 'off'
strip.show();
Serial.begin(9600);
}
int tempCounter = 0;
int tempCounterThresh = 300;
int randy = 0;
void loop() {
tempCounter+=1;
if(tempCounter>=tempCounterThresh){ //This is a counter to periodically trigger some fades.
//startTween(whichLED, durationOfFade, redValue, greenValue, blueValue, brightness);
startTween(0, 300, colourArray[randy][0], colourArray[randy][1], colourArray[randy][2], 50+random(126-50));
//This is how you trigger a fade.
startTween(1, 300, colourArray[randy][0], colourArray[randy][1], colourArray[randy][2], 50+random(126-50));
startTween(2, 300, colourArray[randy][0], colourArray[randy][1], colourArray[randy][2], 50+random(126-50));
startTween(3, 300, colourArray[randy][0], colourArray[randy][1], colourArray[randy][2], 50+random(126-50));
tempCounter=0;
/*randy+=1;
if(randy==13){
randy=0;
}*/
randy = random(13);// number to select a random colour from the colour array
}
for(int k=0; k<nLEDs; k++) {//for each led
if(ledInfo[k][10]==1){ //if tween is active
ledInfo[k][0] +=1; //advance time counter by 1
if(ledInfo[k][0]>=ledInfo[k][1]){ //if time equals the duration. aka the tween has finished
for(int i=0; i<3; i++) { //go through r g and b values
ledInfo[k][2+i] = ledInfo[k][5+i]; //and set old colours to new colours
}
ledInfo[k][10]=0; //set 'tween in progress' to false
}
}
}
updateLEDs();
delay(1);
}
int startTween(byte ledID, int duration, int nR, int nG, int nB, int newBr){
int currentBrightness = linearTween( ledInfo[ledID][0], ledInfo[ledID][8], ledInfo[ledID][9]-ledInfo[ledID][8], ledInfo[ledID][1]);
if(currentBrightness>126){
currentBrightness=126;
}else if(currentBrightness<0){
currentBrightness =0;
}
ledInfo[ledID][8] = currentBrightness;
for(int i=0; i<3; i++) {//loop through the r g and b for that LED
currentRGB[i] = linearTween(ledInfo[ledID][0], ledInfo[ledID][2+i], ledInfo[ledID][5+i]-ledInfo[ledID][2+i], ledInfo[ledID][1]); //working out the current colours using the tween equation
if(currentRGB[i]>126){
currentRGB[i] = 126;
}
if(currentRGB[i]<0){
currentRGB[i] = 0;
}
ledInfo[ledID][2+i] = currentRGB[i]; // set the 'previous R G and B' to the current R G and B values
}
ledInfo[ledID][10] = 1;
ledInfo[ledID][0] = 0;
ledInfo[ledID][1] = duration;
ledInfo[ledID][5] = nR;
ledInfo[ledID][6] = nG;
ledInfo[ledID][7] = nB;
ledInfo[ledID][9] = newBr;
}
void updateLEDs(){
for(int k=0; k<nLEDs; k++) { //loop through all the LEDs
if(ledInfo[k][10] == 1){ //if tween is active
int currentBrightness = linearTween( ledInfo[k][0], ledInfo[k][8], ledInfo[k][9]-ledInfo[k][8], ledInfo[k][1]); //work out the current brightness using the tween equation
for(int i=0; i<3; i++) {//loop through the r g and b for that LED
currentRGB[i] = (linearTween( ledInfo[k][0], ledInfo[k][2+i], ledInfo[k][5+i]-ledInfo[k][2+i], ledInfo[k][1])/126.0) * currentBrightness;
//equation is used here to work out the LED colour values. See the function 'linearTween' below which is used to work out both colour and brightness.
//The colour is worked out first, and is then divided by 126 (number of possible LED colour steps for R, G, and B respectivley).
//This is then multiplied by the brightness value, previously worked out as 'currentBrightness'
if(currentRGB[i]>125){
currentRGB[i] = 126;
} //these restraints to keep the values in an acceptable range
if(currentRGB[i]<1){
currentRGB[i] = 0;
}
strip.setPixelColor(k, strip.Color(pgm_read_byte(&(humanColour[currentRGB[0]])),pgm_read_byte(&(humanColour[currentRGB[1]])),pgm_read_byte(&(humanColour[currentRGB[2]]))));
//setting the pixel colour using the human translation table
}
}
}
strip.show();
//show LED changes
}
//t = current time
//b = start value
//c = change in value
//d= duration
int linearTween(float t,float b,float c,float d) {
return c*t/d + b;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
////// By causticforms Jan, 2013. Modified from an initial Adafruit sketch. http://causticforms.co.uk
////// LPD8806 library written by Adafruit is required which you can find here:
////// https://github.com/adafruit/LPD8806
////////////////////////////////////////////////////////////////////////////////////////////////////
Uhoh. Hardware completed in about 5 hours. I’ll post up a proper spec soon.
So here I’ll briefly explain how the software for the suit works without going into detail, but feel free to get in touch if you have any more in depth questions. The idea was to have the software create patterns akin to a jellyfish that would change with the beat of music. I wish I could release the code for it, but I’m sad to say it’s just a bit too messy.
The linear references for the LEDs were put into a 2D array initially in order for me to easily and correctly reference specific LEDs, with rows aligned horizontally and columns aligned vertically.
Most of the LEDs in the suit, while the program runs, are just ‘messengers’ which take on and pass colours to their neighbouring LEDs. They do this in reference to the direction of a selected row, which we’ll call the ‘origin row’. This behaviour gives the effect of patterns emanating upward and downward from this row. The selected origin row changes while the program runs.
The origin row LED colours are changed depending on the current tempo. The origin LEDs combined with the messenger LEDs create the main visual effect of the suit.
The tempo is set with a keypad. I’ll hopefully post later about why this is the case, as opposed to other methods, but I’ll leave that for now. An assigned button is pressed on the keypad in time with the music. The button is required to be pressed a number of times, as to get an accurate tempo reading the program needs to log the time between multiple key presses and then takes an average.
Most electronic music has a 4/4 time signature, and the software was created with this in mind. There are 3 key things that the program actively monitors. The beats, the bars and sets of 4 bars. Lets say for example we have an averaged tempo value of 80 milliseconds. Every 80 milliseconds that passes the program knows is a ‘beat’, and from this we can calculate the bars. With these pieces of information the program can do it’s magic!
Pretty sure I made this tempo lark sound relatively easy but in fact it was pretty problematic. Controlling more LEDs makes the processor work harder, so when I went from controlling about 50 LEDs in the testing phase, to 250 odd when I started controlling every LED in the suit, the program/Arduino kinda started to struggle. The program started to have trouble keeping the beat for long periods of time, and would drift out after 32 bars or so. I’m still not totally sure where the timing issues lay, but if the Arduino millisecond counter is not effected by processor workload, as I’m lead to believe, then it must be something to do with my program. Anyway, although the beat keeping is not incredibly accurate, it stays on beat long enough to keep me happy with it’s performance.
So imagine a virtual timeline, with a play-head that plays through the timeline to the end. It then loops, to start playing again from the beginning. This repeats. Then imagine a number of cue points are placed randomly on this timeline. The cue points have allocated color values, and when the play-head plays through these cue points, the origin row LEDs change to the allocated colour. They don’t just immediately change though. Instead, to make the visuals look more organic, the previous colour fades to the new colour. I used a simple easing equation for this.
One loop of the timeline lasts for one bar, and this bar is repeated 4 times. After those 4 bars, the cue points on the timeline and colours allocated to them change. This process is repeated indefinitely.
So here’s what I used:

The enclosure as it was, nearly finished.
I’ll go into some detail about how I put this together in an additional post.
Some final tweaks are being made but apart from that it’s ready for Bestival 2012! Exciting! Thanks to @chargeandstore who will be keeping my suit battery charged throughout the festival! They don’t just charge suits either! Check them out.
All information about how it was made, exactly what it does, and any media featuring it will be posted here when I get back.