Sunday, 20 October 2013

Disco Skirt

What child doesn't like lights? When I suggested to my six year old daughter that I could build her a light up skirt for her school disco, she was thrilled. When my wife started telling the other parents of my plans, I had to hurry up and make it.

This post contains the details of how I made the skirt and describes the software used to control it.



The skirt can light up its lights in different ways and also respond to motion and sound. It's easier to show rather than describe, so watch the video.


Credits

The original design for this project is from Becky Stern of Adafruit Industries. She wrote the original Sparkle Skirt tutorial which has clear instructions on how to build a skirt that responds to motion. I followed all of the instructions in that guide to build a basic skirt, and then added a few more components. I won't repeat the build instructions contained in that tutorial, but focus on how mine differs from the original.

I've added a bit more to the design by incorporating a mode switch which changes the skirt functions and by adding a microphone so the skirt can react to sound. I used the Arduino FHT Library to transform sampled sound into frequencies using a Fast Hartley Transform.

In a number of places in the code, I've used Hue, Saturation and Brightness (HSB) to represent LED colours. I used Kasper Kamperman's HSB to RGB function to convert to the RGB values required by the LEDs.

Circuit

The circuit is similar to that described in the Adafruit tutorial and is based around their Flora board. The Flora is an Arduino compatible microcontroller board that is designed for wearable projects. Most notably, it has evenly spaced pads that can be easily sewn to using conductive thread.

The project contains:
The layout of the circuit is shown in the diagram below.


The accelerometer communicates using an I2C interface and is connected to the SCL and SDA pins of the Flora, as well as 3.3V and ground for power.

The microphone, in addition to 3.3V and ground connections has its output connected to Flora pin D10. D10 also doubles as analog input A10, and we read an an analog voltage to on this pin to determine the microphone level.

The LEDs are all connected VBATT, which is the unregulated battery voltage. The Neo Pixels are nominally 5V devices, however they will work at lower voltage levels with reduced brightness. LiPo batteries have a nominal voltage of 3.7V with a maximum of about 4.2V, and drives these LEDs with sufficient brightness. Using For the data connection, the first LED in the chain is connected to D6 of the Flora. From there, the output of the previous LED is connected to the input of the next LED.

The battery is connected to the Flora's JST battery port and, the switch is connected between D9 and ground.


Construction

The circuit is sewn, rather than soldered together using stainless steel conductive thread. The thread takes the place of wires or copper traces that are used to connect components in a normal circuit. The components are sewn on the inner layer of a skirt with a mesh outer lay providing some light diffusion. Adafrut has a great guide to working with conductive thread which was useful is sewing the circuit together. I highly recommend reading it you're planning to build something similar.

It was the first time I'd attempted sewing and I made quite a few mistakes. I managed to sew to the wrong pads on the LEDs on a number of occasions, so it pays to double and triple check your connections to save having to re-sew them. I spent about 10 hours sewing this together, though I'm sure my skills improved by the end and I could make this much faster if I ever made another one. Using an embroidery hoop really helped keep things stable while sewing to them.

The microphone breakout board is not designed to be incorporated into a wearable design, but I had no problems sewing to the solder pads.

I had a bit of trouble deciding how to attach the switch to the Flora. I wanted a connection that didn't require soldering as I had already sewn the Flora to the skirt at this point and didn't want to bring a hot soldering iron close to the fabric. I ended up crimping a couple of ring terminals to the switch connectors and sewing conductive thread onto the ring. I then stitched the switch in place with non-conductive thread.


Software

My software is based on the Adafruit sparkle skirt software, but has been enhanced to make it easy to add and remove different modes. I've published the source code on GitHub.

The skirt can perform a number of functions and switches between them when the button is pressed. The modes are:

  • Sparkle with movement. This is the original Adafruit code which flashes random pixels when motion is detected via the accelerometer.
  • Colour wheel cycle. The LEDs are mapped to equally spaced angles on a colour wheel. The colour wheel is then "rotated" through the LEDs so that each LED runs through the complete colour wheel in 3.6 seconds.
  • Fade in and twinkle. A random colour is picked and all LEDs fade from black to the colour. Once maximum intensity is reached, LEDs are randomly twinkled and then all LEDs fade to black.
  • Chaser. A random colour is chosen and all LEDs are turned on, one at a time, in sequence.
  • Sync to sound. Sound is sampled through the microphone and a Hartley transform is used break the input into frequency bands. Each frequency band is mapped to a colour and all LEDs are set to the colour corresponding to the frequency band with the highest level.
Some of the more interesting parts of the code are discussed below.

Modes

The code to change modes is shown below.

 void(*modes[]) () = { &sparkleWithMove,  
            &colorWheelCycle,  
            &fadeAndSparkle,  
            &chase,  
            &soundVisualisation };  

The mode array contains function pointers to each of the modes. The functions should be void returning methods that take no arguments. Functions can be added or removed from this array to change the skirt modes.

The variable changeMode should be checked periodically to see if the mode should exit. If this variable is 1, then the interrupt handler has detected a button press and the mode should return to the main loop which will switch to the next mode and clear the changeMode flag. A good place to check is during delays when the code is idle.

The DELAY macro can be used as a drop-in replacement of the delay() function. This splits the large delay into smaller delays and checks the changeMode flag. If a mode change is detected, then the macro will return out of the function.

Sound Visualisation

The sound visualisation code is interesting in its audio sampling. The analogRead() function is quite a slow way to take read a value, so the Analog to Digital Converter (ADC) is set to free-running mode where it continually takes readings. By sampling faster, the range of frequencies that the skirt can respond to is increased. The skirt seems to respond to frequencies of up to about 5kHz, which means, using Nyquist's theorem that we are sampling at around 10kHz. The following code sets the sampling to free-running mode by setting the AVR registers.

  ADCSRA = 0xe5; // set the adc to free running mode  
  ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((13 >> 3) & 0x01) << MUX5);  
  ADMUX = 0x40 | (13 & 0x7); // use adc13  
  DIDR2 = 0x10; // turn off the digital input for adc13
   

Note that the analog 10 pin to which the microphone is connected needs to be converted to the correct ADC number "under the hood". This can be found be referring to the Flora pinout diagram. We also disable the digital input on the analog input as recommended by Open Music Labs.

We take the required number of samples from the ADC pin. while we are taking samples, TIMER0 is disabled to reduce sampling jitter. We then check the ADCSRA register to check whether it's ready for a sample to be taken. Once the data is ready, we restart the ADC and get the high and low bits of the sample. We covert the 10 bit value to a signed number, as required for the transform, and then scale it up to a 16 bit value.

   TIMSK0 = 0; // turn off timer0 for lower jitter  
   for (int i = 0 ; i < FHT_N ; i++) {
    while(!(ADCSRA & 0x10)); // wait for adc to be ready  
    ADCSRA = 0xf5; // restart adc  
    byte m = ADCL; // fetch adc data  
    byte j = ADCH;  
    int k = (j << 8) | m; // form into an int  
    k -= 0x0200; // form into a signed int  
    k <<= 6; // form into a 16b signed int  
    fht_input[i] = k; // put real data into bins  
   }  
   TIMSK0 = 1; // turn on timer0 so we can use millis() again  
   

After sampling, we perform a Fast Hartley Transform to covert the signal into frequencies. I set up the conversion to give 8 frequency bins, each covering an octave. This needs to be set up at the beginning of the sketch, before the FHT.h library is included.

 // FHT configuration for sound visualisation  
 #define OCTAVE 1  // Group buckets into octaves  
 #define OCT_NORM 0 // Don't normalise octave intensities by number of bins

We use the FHT functions to process the sample data. As we are using octave outputs, we have 8 outputs to process. We check to see which octave has the highest intensity and set all LEDs to the colour that corresponds to the octave.

   fht_window();  
   fht_reorder();  
   fht_run();  
   fht_mag_octave();  
     
   int index = 0;    
   int mx = fht_oct_out[0];  
   for (int i = 1; i < 8; i++) {  
    if (fht_oct_out[i] > mx) {  
     index = i;  
     mx = fht_oct_out[i];  
    }  
   }  
     
   int r = fhtColors[index][0];  
   int g = fhtColors[index][1];  
   int b = fhtColors[index][2];  
     
   for (int i = 0; i < numPixels; i++) {  
    strip.setPixelColor(i, r, g, b);  
   }  
     
   strip.show();  
   

Further Work

There are a couple of things that I'd like to improve with this project. Firstly, there's always room for more or improved modes, especially when responding to sound. Trying some sort of beat detection is an obvious next step.

Whilst I like the mode switch, I think I'l like to try a capacitive touch sensor instead of a hard plastic button. I could then set to a touch sensor breakout with some conductive fabric to create a soft button.