Shimmering Chameleon (smart)Skirt ~

by cdstudioNH in Craft > Fashion

3774 Views, 36 Favorites, 0 Comments

Shimmering Chameleon (smart)Skirt ~

The Shimmering Chameleon (smart)Skirt
20201218_102722.jpg

I love to sew and I'm on the LED bandwagon, oh, and it's fashion show season. This would be a unique Prom Outfit, for sure! I created this skirt using neopixels, a Flora, a color sensor and an accelerometer. The color sensor takes a picture of whatever you put in front of it and translates that through the attached neopixels! I also attached an accelerometer so that when you move, the neopixels sparkle that color. Read on to see how I made it!

The Code

#include <Wire.h>
#include <Adafruit_TCS34725.h>
#include <Adafruit_LSM303.h>
#include <Adafruit_NeoPixel.h>

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_RGB     Pixels are wired for RGB bitstream
//   NEO_GRB     Pixels are wired for GRB bitstream
//   NEO_KHZ400  400 KHz bitstream (e.g. FLORA pixels)
//   NEO_KHZ800  800 KHz bitstream (e.g. High Density LED strip)
#define NUM_PIXELS 4
Adafruit_NeoPixel strip = 
    Adafruit_NeoPixel(NUM_PIXELS, 6, NEO_GRB + NEO_KHZ800);
Adafruit_TCS34725 color_sensor = 
    Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);
Adafruit_LSM303 accel;

#define STILL_LIGHT // define if light is to be on when no movement.
                    // Otherwise dark

// our RGB -> eye-recognized gamma color
byte gammatable[256];
int g_red, g_green, g_blue; // global colors read by color sensor
int j;

// mess with this number to adjust TWINklitude :)
// lower number = more sensitive
#define MOVE_THRESHOLD 45
#define FADE_RATE 5

int led = 7;
double newVector;

void flash(int times)
{
    for (int i = 0; i < times; i++)
    {
        digitalWrite(led, HIGH);  // turn the LED on (HIGH is the voltage level)
        delay(150);               // wait for a second
        digitalWrite(led, LOW);   // turn the LED off by making the voltage LOW
        delay(150);
    }
}

float r, g, b;
double storedVector;

void setup()
{
    pinMode(led, OUTPUT);
    // Try to initialise and warn if we couldn't detect the chip
    if (!accel.begin())
    {
        Serial.println("Oops ... unable to initialize the LSM303. Check your wiring!");
        while (1)
        {
            flash(4);
            delay(1000);
        };
    }

    strip.begin();
    strip.show(); // Initialize all pixels to 'off'

    if (!color_sensor.begin()) 
    {
        Serial.println("No TCS34725 found ... check your connections");
        while (1)
        {
            flash(3);
            delay(1000);
        };
    }

    // thanks PhilB for this gamma table!
    // it helps convert RGB colors to what humans see
    for (int i = 0; i < 256; i++) {
        float x = i;
        x /= 255;
        x = pow(x, 2.5);
        x *= 255;

        gammatable[i] = x;
    }

    //this sequence flashes the first pixel three times 
    // as a countdown to the color reading.
    for (int i = 0; i < 3; i++)
    { 
        //white, but dimmer-- 255 for all three values makes it blinding!
        strip.setPixelColor(0, strip.Color(188, 188, 188)); 
        strip.show();
        delay(1000);
        strip.setPixelColor(0, strip.Color(0, 0, 0));
        strip.show();
        delay(500);
    }

    uint16_t clear, red, green, blue;

    color_sensor.setInterrupt(false);      // turn on LED

    delay(60);  // takes 50ms to read 

    color_sensor.getRawData(&red, &green, &blue, &clear);

    color_sensor.setInterrupt(true);  // turn off LED

    // Figure out some basic hex code for visualization
    uint32_t sum = red;
    sum += green;
    sum += blue;
    sum = clear;
    r = red; r /= sum;
    g = green; g /= sum;
    b = blue; b /= sum;
    r *= 256; g *= 256; b *= 256;

    g_red = gammatable[(int)r];
    g_green = gammatable[(int)g];
    g_blue = gammatable[(int)b];

    // Get the magnitude (length) of the 3 axis vector
    // http://en.wikipedia.org/wiki/Euclidean_vector#Length
    accel.read();
    storedVector = accel.accelData.x*accel.accelData.x;
    storedVector += accel.accelData.y*accel.accelData.y;
    storedVector += accel.accelData.z*accel.accelData.z;
    storedVector = sqrt(storedVector);
}

void loop()
{
    // get new data
    accel.read();
    double newVector = accel.accelData.x*accel.accelData.x;
    newVector += accel.accelData.y*accel.accelData.y;
    newVector += accel.accelData.z*accel.accelData.z;
    newVector = sqrt(newVector);

    // are we moving 
    if (abs(newVector - storedVector) > MOVE_THRESHOLD)
    {
        colorWipe(strip.Color(0, 0, 0), 0);
        flashRandom(10, 25);    // first number is 'wait' delay, 
                                //     shorter num == shorter twinkle
                                // second number is how many neopixels to 
                                // simultaneously light up
    }
    
#ifdef STILL_LIGHT
    else
    {
        colorWipe(strip.Color(gammatable[(int)r], 
            gammatable[(int)g], gammatable[(int)b]), 0);
        storedVector = newVector;
    }
#endif
}

void flashRandom(int wait, uint8_t howmany) 
{
    for (uint16_t i = 0; i < howmany; i++) 
    {
        for (int simul_pixels = 0; simul_pixels < 8; simul_pixels++)
        {
            // get a random pixel from the list
            j = random(strip.numPixels());
            strip.setPixelColor(j, strip.Color(g_red, g_green, g_blue));
        }

        strip.show();
        delay(wait);
        colorWipe(strip.Color(0, 0, 0), 0);

        // now we will 'fade' it in FADE_RATE steps
        for (int x = 0; x < FADE_RATE; x++) 
        {
            int r = g_red * (x + 1); r /= FADE_RATE;
            int g = g_green * (x + 1); g /= FADE_RATE;
            int b = g_blue * (x + 1); b /= FADE_RATE;

            strip.setPixelColor(j, strip.Color(r, g, b));
            strip.show();
            delay(wait);
        }

        // & fade out
        for (int x = FADE_RATE; x >= 0; x--) 
        {
            int r = g_red * x; r /= FADE_RATE;
            int g = g_green * x; g /= FADE_RATE;
            int b = g_blue * x; b /= FADE_RATE;

            strip.setPixelColor(j, strip.Color(r, g, b));
            strip.show();
            delay(wait);
        }
    }
#ifdef STILL_LIGHT
        colorWipe(strip.Color(gammatable[(int)r], gammatable[(int)g], gammatable[(int)b]), 0);
#endif
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, c);
        strip.show();
        delay(wait);
    }
}

The Test

IMG_2777.JPG
IMG_2779.JPG
IMG_2780.JPG
IMG_2781.JPG
IMG_2783.JPG
Shimmering Chameleon test for instructables

First I needed to conduct some tests for what I wanted to accomplish, using the materials I had. I had to test the resistance of the conductive thread for the distances I was going.

I made a mock-up on the sewing machine, creating 3 rows of conductive thread, doubled up. Stainless steel is very strong, but it has a lot of resistance and can shift and become loose around pin-outs. I wanted to see the max distance the info could travel along this set-up.

I set everything up with alligator clips to test the thread and make sure the code still worked as it was supposed to.

Supplies and Fashioning

IMG_2793.JPG
IMG_2794.JPG
IMG_2795.JPG
IMG_2797.JPG
IMG_2798.JPG
IMG_2799.JPG

First I made a skirt out of a stretchy spandex fabric that was futuristic silver, and just used the wrong side. I had some leftover gold stretchy silk from when I hemmed a couture wedding dress and got to keep the cutoff. This allows me to have the skirt fit a range of sizes, BUT also means that I have to do a lot of hand-sewing. I chose to put the Party of the skirt at the bottom, which has a large enough circumference to be unaffected by the stretch limitations.

Most of the electronics are from Adafruit. The color-tip glove I made from a pair of beautiful gloves from the 40's. I love mixing old with new!

I marked with disappearing ink where I wanted all the neopixels to go. I then sewed them on with a small amount of regular and conductive thread. The real connections would come later.

The Brain

IMG_2800.JPG
IMG_2801.JPG
IMG_2804.JPG
IMG_2805.JPG
IMG_2806.JPG
IMG_2916.JPG

This combo is the brain of the system. It is a Flora, attached to the Color Sensor attached to the Accelerometer/compass.

I sewed everything with the 2-ply conductive thread, making solid connections with plenty of passes and spacing lines sufficiently apart.

For the pin-out connections of data, power and ground that would be traveling down the length of the skirt, I used beefier, insulated wire. You could just use the 3-ply conductive thread, but you'd need to have at least 6 wires bundled together for each of the three lines.

I then sewed the patch onto the skirt, feeding the battery wires though a small opening and into a pocket I created for the LiPo. The battery is 1200mAh, 3.7V.

My Work Is Cut Out for Me

IMG_2812.JPG
IMG_2874.JPG
IMG_2875.JPG
IMG_2911.JPG
IMG_2913.JPG

The first pic is the first neopixel of the 8 neopixel train. The rest would all be connected with 3-ply stainless steel conductive thread, strung with semi-precious stones and beads. These would all add a nice weight and rigidity, plus look more intentional than just dotted lines of wire connecting the neopixels.

The trick was finding a needle eye big enough to accept the steel thread but small enough to fit though the stones/beads. There were a lot of beads that didn't make the cut. : D

The Underneath

IMG_2914.JPG
IMG_2915.JPG
IMG_2821.JPG

I wanted to cover all the exposed thread so as to minimize any chance of shorts. I used iron-on interfacing, clipping to allow curves, and ironed on with a single layer of cheesecloth between the skirt and iron. The initial wires I just wove in between the serged seam.

In Action!

The Shimmering Chameleon (smart)Skirt

I have entered this into the Code Creations Contest and would love a vote if you think I deserve one!

: D

~ Cynthia