causticforms

something completely unrelated

You can see some of the cooler effects in this one.

art

Cuttlefish

 

causticforms-cuttlefish

causticforms © 2013

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:

  • duration of the fade
  • colour
  • brightness

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
////////////////////////////////////////////////////////////////////////////////////////////////////
RGB LED Moustache

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.

Basics

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.

Setting the tempo

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.

The ‘origin’ LEDs

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:

  • Arduino Nano (ATmega328) - I started the project with an Arduino Uno but switched to a Nano in the final stages so it would fit into a small enclosure.

    The enclosure as it was, nearly finished.

  • 241 WS2801 RGB LEDs (flat style) – from Adafruit – these guys have the BEST support and very reasonable pricing, even when shipped to the UK, as per my case.
  • 12v to 5v step down converter – from Ebay, shipped from China. Despite my initial reservations this works as expected. Sweet!
  • 12mm white plastic eyelets – I asked for a few initially to test how the LEDs would fit and… turned out they were the perfect size and very light as well! I don’t know what I’d have done without these. They’re from LangarDirect. Very reasonable pricing too!
  • Tracer 12v 8Ah Lithium Polymer Battery Pack – from DEBEN – Bit of a premium price compared to other brands, but the quality, features and for peace of mind it was a no-brainer. I say peace of mind because lithium polymer batteries can be dangerous if not handled properly, and ideally I wanted to avoid going up in flames. It’s got a charge guage built in which I found very handy. I also bought some 5.5mm DC connectors from them which come pre-wired at a specified lengths to connect the battery to your electronics.
  • Silicone Wearable Keypad – Great unit, but the code provided by the retailer to get it working wasn’t great and contained a bug. Luckily someone else had problems with this too and they fixed it! You can find that code here. Thanks chebe!
  • Serial Connectors – Female and Male (9-pin) for Ribbon Cable. These are to connect the keypad. These required no soldering, which was… nice.
  • White Dickies Redhawk Boilersuit – Had some sizing issues around the groin area here (common problem) but added some material between the seams which I cut out of the knees (the knees are extra padded), so it fitted better. There are quite a few pockets in this boiler suit. The two at the front were great and I managed to fit the electronics in one, and the battery in the other, however the others weren’t of any use. In retrospect I should have cut these out before starting work, as maneuvering the LED strands in and out of holes in pockets so that the LEDs are always sitting behind the outermost layer of material was a pain.
  • Wires of various current – I went OTT current wise on pretty much all my wiring just to be safe.
  • 5.5mm DC connectors (Male and female) – Here’s where I made an incredibly stupid mistake. I decided to use these connectors for the power in to the electronics, the power out to the LEDs, and also the data out to the LEDs so I could quickly and easily blow up various parts of the extremely expensive suit by plugging wires into the wrong sockets. Note sarcasm. This cost me a lot of money as I blew up half the LEDs at one point. I will never be doing this again.
  • An enclosure for housing the voltage converter, arduino, and power management bits.
  • Various bits for the power management stuff – (2 x nut and bolt, insulated eyelets)

 

I’ll go into some detail about how I put this together in an additional post.

suitDone

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.