Monday, September 5, 2011

recirculating snow globe


Apart from the actual figurine to go in the thing, I've mostly got this recirculating snow globe together.  I've put an adjustable flow aquarium pump into a separate glass container that has an opening the same size as the gasket for the bottom of the globe.  Sealing it all up will be a bit of a trick.  I've tested it with nothing but duct tape around the base, though, and apart from a little leaking around the power cord cutout, it worked great!

Meet Professor Fuzzles!


Professor Fuzzles is an animatronic bunny head that looks around and sniffs.  Well, a proper bunny sniff would scrunch the nose, but instead it's moving its head a bit up and down.  Close enough!  This head will get reunited with its body once I mount everything on its display shelf.

Here's the stuffed bunny head I started with.  I liked that it had a slightly miffed-looking expression, and thought that might work nicely in the haunted house.

Disapproving bunny disapproves!

I replaced its eyes with a pair of my slightly larger toy eyes modified to glow by adding a pair of LEDs behind each eye.  I put yellow LEDs behind a pair of green eyes.

After eye replacement surgery.
I intend to power the eyes through a wall wart plugged in to a Power Switch Tail, so I can control turning them on and off from an Arduino or other microcontroller.

Here the eyes are lit.

I've given the Professor a top hat, found in a hobby store.  The head is sitting on top of a pair of standard-sized servos in a pan-and-tilt bracket assembly, mounted on a Maker Beam frame.  I'd started out using micro servos, which fit better in the head, but the gears stripped out before long.  The standard sized servos seem to be plenty tough for Professor Fuzzles' noggin.




Sunday, August 21, 2011

glowing toy eyes

So I want to give my animatronic critters glowing eyes.  I've got an eye together now that I think will serve the purpose.  It's a toy eye with two LEDs stuck between the eye and the backing piece, with cardboard wrapped in foil around the sides.


Here are the LEDs before they went around the eye.  I've soldered a 47 Ohm resistor to one leg of each of the yellow LEDs.  The foil-wrapped cardboard piece is taped to one of the LEDs here.  I'm using speaker wire to hook it all up.


The painted eye diffuses the LED light well, and the yellow light behind a turquoise-painted eye produces a nice effect.  Although not very bright, in the dark of the haunted house these eyes should show up well.

I intend to wire up a matching eye for this one, and mount them both inside a bunny head.  The bunny head I want to put on a pan-and-tilt bracket, to make it look around.

Hullabaloo, the flapping monkey


Update to the flapping monkey project:  it flaps, and it has a name, Hullabaloo.  There's still work to do to get it looking better.  The body can be arranged around the frame to hide the shoulder joints a bit better.  Also, the fabric from the head is just sitting loose on top at the moment.  I'm considering giving Hullabaloo a different head, from another animal.

Here I'm using an Arduino Mega to send commands to the SSC-32 servo controller, so it's nice to have that working.  I'll likely use the SSC-32 to run multiple toys.

Tuesday, August 16, 2011

purple flappy monkey, mostly there

Although this poor purple stuffed monkey has done nothing to deserve it, I've cut off its arms and put them on hinges.  I'm working on making its arms flap up and down.

Here is the framework for it with one flapping arm attached.




Clothespin hinge
I've hinged together the two wooden pieces of a clothespin by gluing on a small nylon hinge sold for use in model aircraft, then sewn one end into the arm, and attached the other end to the Maker Beam frame with a couple of zip ties.  The bottom of the clothespin hinge rests against a couple of screws in the frame.  At the shoulder is a control horn (also sold for use with model aircraft) which is attached to the servo horn with some fishing line.



I'm controlling it right now with an SSC-32 servo controller.



At the moment I've got the second arm attached and the base inside the monkey.  I just need to cut some holes behind the ears to accomodate the fishing line moving up and down, and get the head situated around the frame.  Then it's flappy monkey time!


Saturday, August 13, 2011

blinking cabbage patch doll


Here I've connected the eyes of a cabbage patch doll to a servo, so I can control opening and shutting her peepers.  The doll came with weighted eyes that shut when she's reclining, and open when she's upright.

I started out by cutting out a piece of the back of the head, to get acces to the eyes.  The eyes sit in molded sockets that are part of the rubber head.  I slit open the socket backs, but left them mostly intact so I could put the eyes back in them.  The eyes had a black plastic cover on the back that connects to the metal case surrounding the eye in the front.  I sliced off most of the black plastic cover with a Dremel tool, but left a ring of it to hold the works together.




To connect to the eyes, I superglued a large bent paperclip to the back of each of eye.


Then I mounted them back into the sockets in the head.  I cut off the ring on the base of the neck, as it was interfering with the paperclip travel.  This picture was taken through the hole cut in the back of the head.



I bent the hanging ends of the two paperclips and taped them together, so I could control them with one servo.



The servo I mounted to a base built from Maker Beam.  I attached the servo arm to the paperclips using a zip tie and some fishing line, so it could have a little play.  Here is the head with the eyes moving while sitting on the bare base.


I cut the backs of the doll's legs and pulled out a bit of the stuffing, then shoved the two beams at the bottom of the base into the legs.  I cut the doll's back and pulled out most of the stuffing in the body, then sewed it mostly back up.  I left the neck partially open to pull over the two beams at the top of the base structure.  Here is a picture from the back of the assembly.


For now, I'm using a Pololu Maestro servo controller with its software to test.  I intend to try out some others, and figure out how I want to connect together several dolls and stuffed animals for a display.

So there it is!  The eyes move slower than I might like, and also don't open and close completely, but I'm happy enough with the project as it is.

Tuesday, August 2, 2011

Frankenshirt

This is a shirt I made last November that changes the colors of the cat's eyes when the right wrist is rotated up or down.


A LilyPad Arduino board controls it, powered by one AAA battery.  An accelerometer on the right cuff detects movement.  When the right hand is hanging down, the cat's eyes glow red.  As the hand is raised, the color of the cat's eyes moves through the color spectrum.  When the hand is raised perpendicular to the ground, the eyes glow violet.

The eyes are two RGB LEDs, covered with a piece of plastic to diffuse the light.

Here's a video of the thing in action:


Making Frankenshirt

To start, I made a stencil of the cat outline, then painted the image onto a second shirt using spray fabric paint.  Spray stencil temporary glue came in handy for this.  The cat outline is based on a drawing in the Neil Gaiman graphic novel series, The Sandman.


I cut a square out of this shirt around the cat image to place over the electronics on the dark brown shirt.

I started sewing the electronics into the dark brown shirt by sewing a strip of conductive braid down the right arm, as well as a strip of conductive fabric on either side of the braid.  The braid has three separated tinsel lines in it, so between the braid and the two strips of conductive fabric, I had five lines to connect the accelerometer on the cuff to the LilyPad on the shoulder.  These lines are for the x, y, and z axes, and for power and ground connections.  As I'm only using two axes for rotation detection, I could have left out one line on the sleeve.

I used conductive thread to sew in the conductive fabric.

accelerometer sewed onto shirt cuff
Next I sewed the LilyPad and power board onto the shoulder.  I put the battery next to the LilyPad so it could have a good power connection.  Due to flexing of the fabric around the arm, I went back to the conductive fabric power and ground connections to reinforce them, eventually soldering on some insulated wire.  I covered all the conductive bits on the sleeve in glow-in-the-dark puff paint for insulation.

To connect the LilyPad to the two RGB LEDs on the front, I sewed a length of conductive braid and a strip of conductive fabric across to the front.  The end of the braid I cut up to separate out the three tinsel lines, which I then sewed to the shirt.  I insulated the LED lines with black fabric paint.


I cut out a bit of a plastic milk jug a friend saved for me to use, and sewed it over the LEDs.  Then I finished the design by attaching the square of fabric with the cat design, using spray fabric glue, and sewed around the edges of the square.

Where's the code?

Oh, here it is!



/*
 * Change color of an RGB LED based on the angle of rotation
 * of y/x from an accelerometer, so as to move through the full
 * color spectrum as the accelerometer turns 180 degrees.
 *
 * Used with an ADXL335 analog three-pin three-axis accelerometer.
 *
 * Based on the Sleeping Arduino sketch by Ed Halley.
 * (The Pummer RGB interpolation class is unchanged.)
 *
 * Released (cc) Creative Commons Attribution Only
 * Kathryn Killebrew
 * 
 */

#include 

/* Pummer:
 * A simple RGB color-interpolating helper class.
 *
 * When creating one, tell it which three output pins to drive PWM signals.
 * If your RGB device is common-anode, it can reverse the PWM for you.
 * Don't forget to limit current to each LED with a resistor (e.g., 220ohm).
 *
 * At any time, tell it what color to become by calling the goal() method,
 * and how fast to transition to that color.
 *
 * Call the pummer's loop() method occasionally to let it set the PWM
 * outputs to the LEDs.
 */
class Pummer
{
    byte lR, lG, lB;
    byte nR, nG, nB;
    byte wR, wG, wB;
    int pR, pG, pB;
    unsigned long last, when;
    boolean reverse;

public:
    Pummer(int pinR, int pinG, int pinB, boolean anode=false)
    {
        pinMode(pR = pinR, OUTPUT);
        pinMode(pG = pinG, OUTPUT);
        pinMode(pB = pinB, OUTPUT);
        nR = nG = nB = 0;
        reverse = anode;
        show();
        goal(255, 255, 255);
    }

    void show()
    {
        analogWrite(pR, reverse? (255-nR) : nR);
        analogWrite(pG, reverse? (255-nG) : nG);
        analogWrite(pB, reverse? (255-nB) : nB);
    }

    boolean done() { return last == when; }

    void goal(byte r, byte g, byte b, unsigned long speed = 500)
    {
        lR = nR; lG = nG; lB = nB;
        wR = r; wG = g; wB = b;
        last = millis();
        when = last + speed;
    }

    void loop()
    {
        unsigned long now = millis();
        if (now > when)
        {
            if (last == when)
                return;
            nR = wR; nG = wG; nB = wB;
            last = when;
        }
        else
        {
            nR = map(now, last, when, lR, wR);
            nG = map(now, last, when, lG, wG);
            nB = map(now, last, when, lB, wB);
        }
        show();
    }
};

/* Accelerometer:
 * Receive input from a 3-axis device, and perform some useful calculations.
 *
 * Specify the three axis pins using analog pin numbers.
 * These are usually adjacent on the common breakout boards.
 *
 * Call the accelerometer's update() method occasionally to update the
 * current values from the hardware.
 */
 
#define ANALOG0 14

class Accelerometer
{
    int p[3]; // which analog pins
    int a[3]; // acceleration, zero-based
    int b[3]; // acceleration bias/calibration information
    float r;  // angle of rotation

public:
    Accelerometer(int pinX, int pinY, int pinZ)
    {
        pinMode((p[0] = pinX) + ANALOG0, INPUT);
        pinMode((p[1] = pinY) + ANALOG0, INPUT);
        pinMode((p[2] = pinZ) + ANALOG0, INPUT);
        
        for (int i = 0; i < 3; i++) {
            b[i] = 512;
        }
        
        r = 0;
    }

    void update()
    {
        for (int i = 0; i < 3; i++) {
             a[i] = analogRead(p[i]) - b[i];
        }
        
        r = 0;
    }

    void dump()
    {
        Serial.print(  "x="); Serial.print(a[0]);
        Serial.print("\ty="); Serial.print(a[1]);
        Serial.print("\tz="); Serial.print(a[2]);
        Serial.print("\troll="); Serial.print(roll());
        Serial.println();
    }

    int accel(int axis)
    {
        if (axis < 0 || axis > 3) return 0;
        return a[axis];
    }

    float roll()
    {
        if (r != 0) return r;
        r = atan2(a[1], a[0]); // rotation of y / x
        return r;
    }
};

void loop() { ; } // we do our own loop below

void setup()
{
    Serial.begin(9600);
    
    byte newColor[3] = {0,0,0}; // RGB values, 0 - 255
    float angle;                // angle of rotation, in radians
    int colorPlace;             // angle mapped to range of color values
    int rainbowState;           // which state of change color is in
    byte incColor;               // if adding color to mix, how much
    byte decColor;               // if removing color from mix, how much
    int div = 0;                // counter for averaging angle readings
    int numReads = 8;           // number of readings to average
    
    float rollReads = 0.0;      // running total of rotation angle readings
    
    // initialize with pin numbers for LED colors and accelerometer axes
    Pummer pummer = Pummer(4, 3, 2, true);            
    Accelerometer accel = Accelerometer(A0, A4, A2);
    
    while (1)
    {
        delay(20);
        
        accel.update(); // read accelerometer axes
        
        angle = accel.roll(); // get a rotation angle reading
        angle = abs(angle); // show full spectrum in 180 deg. (have angle in radians, ranges from -pi to pi)
        rollReads += angle; // accrue readings for average
        
        if (--div <= 0) { 
          
         angle = rollReads / numReads; // get the average angle reading
         rollReads = 0;
         
        // map angle to range of color values with floating-point math
        colorPlace = (int)( angle * 1535.0 / M_PI + 1 ); 
        // what in-built map function does, with int math
         //(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        
        colorPlace = map(colorPlace, 0, 1535, 1535, 0); // reverse range for red at bottom, violet at top

        
        rainbowState = colorPlace / 256; // bin color number into change state
        incColor = colorPlace % 256;     // remainder is amount of partial color to fade in
        decColor = map(incColor, 0, 255, 255, 0); // reverse partial color range for fading out
        
        // make a rainbow
        if (rainbowState == 0) {
          // red to orange
          newColor[0] = 255;
          newColor[1] = incColor;
          newColor[2] = 0;
        } else if (rainbowState == 1) {
          // orange to green
          newColor[0] = decColor;
          newColor[1] = 255;
          newColor[2] = 0;          
        } else if (rainbowState == 2) {
          // green to teal
          newColor[0] = 0;
          newColor[1] = 255;
          newColor[2] = incColor;
        } else if (rainbowState == 3) {
          // teal to blue
          newColor[0] = 0;
          newColor[1] = decColor;
          newColor[2] = 255;
        } else if (rainbowState == 4) {
          // blue to purple
          newColor[0] = incColor;
          newColor[1] = 0;
          newColor[2] = 255;
        } else if (rainbowState == 5) {
          // purple to red
          newColor[0] = 255;
          newColor[1] = 0;
          newColor[2] = decColor;
        } else {
          // red
          newColor[0] = 255;
          newColor[1] = 0;
          newColor[2] = 0;
        }        
        
        // show colors
        pummer.loop();
        if (pummer.done())
        {
          pummer.goal(newColor[0], newColor[1], newColor[2], 100);
        } 
          
        // reset counter for readings
        div = numReads; 
          
        // print values
        accel.dump();
        }         
    }
}