A robot costume in the making

My wife and I are making a robot Halloween costume and I just finished the electronics design prototype. I wanted to wait until the costume was finished before revealing it but I'm too excited that I finished my circuit and coding.

That's a video of it, and I want to describe it a little. It uses an Adafruit Wave Shield for the sounds, and an antique analog gauge I found at Gateway Electronics here in St. Louis. There's an infrared beam pair from Sparkfun, which will be watching the "candy input slot" on the robot's chest. The gauge displays the count, until too much candy is inserted and it goes crazy. Then after 20 seconds of no more candy, the candy count gets reset, to be ready to do it all over again at the next house.

In the sketch, I used these AlphaBeta libraries: LED, button, TimedAction, and Scheduler. These made coding this sketch very easy. He even updated Scheduler with a clear() for me. Thanks again for that! The hardest part was getting it all to work together. I had been trying to use pin 9 for the gauge, and it crashed the sketch. I did more reading and found out you can't do PWM on 9 with the Wave libraries because of the timer. Pin 6 works fine though.

The beep sound loop I found on my mac, I think it came from iMovie. The speech is recorded synthesis also from my mac, made like this from a terminal:
say -o outputfile.aiff "thing to say"

This makes an aiff file. Then I used iTunes to convert all the sound files to WAV with the right settings for the Wave Shield.

The chaser LEDs are going to surround the Arduino in a shadow box sort of thing, so people can see the controller, and to punch it up so it isn't so boring. ;D

Next I have to install all of this in the costume and solder it all up.

When you said Gateway Electronics, it brought back so many good memories. Back in the 80s and early 90s, I lived about 80 miles from there, but would make the trip every two or three weeks to see what goodies they had. You could find almost anything you need or want there...

Standard components? Check.
ILS radar beacons? Check!
Paper tape reader? Check!
4 square feet of double sided blank photosensitive PCB and etchant? Check!

I miss that place, I haven't found anything like it in Florida. Glad to hear they are still in business.

Hope you post your finished project here when you're done.

That is very, very awesome.

Hows the battery life with the speaker being constantly used?

Mike: Gateway is awesome. I started going there in the mid '80s. On my first trip I found a bin full of the grip for my dad's video camera. (Pre-camcorder days, so this grip was a structural feature.) This was a handle with the pause button and zoom controls, and my dad's friction lock had broken so the camera became pretty hard to use. And they had a whole bin full of them! I was hooked.

The first time I took my wife there, she said, "this is like ArtMart for geeks!" Dead-on assessment, I thought.

They've moved from that old location to the Westport area, but it's still the same store, really. The barber shop backwards clock is behind the counter, they still have boxes and boxes of tubes, etc. I think some of their stuff is overpriced, like LEDs are still a buck apiece. But if you need something NOW, you won't find it anywhere else in the area. And for surplus that doesn't exist anymore, well, if you need it, you'll buy it. That gauge I used was $5, and I had bought four different ones a while back and finally have a use for one.

Merve: I haven't tested the runtime yet, but I have a feeling it will last the hour or so my almost three-year-old will last in the costume.

Thanks for the compliments!

Ok, I left it running on a fresh 9v while we went out to lunch, and it sounded just as good over two hours later. So, I think it should do just fine for trick-or-treating. I will carry a spare battery though, just to be on the safe side.

jerry,

way too cool! how did you get the sound again? i'm a total newbie here btw.

thanks!

We finished the costume, just barely in time for trick-or-treating.
Here's some photos, and a video of the electronics in action.

neosurreal: The sound is driven with an Adafruit Wave Shield, and the sounds were a loop I found on my Mac, as well as some recorded speech synthesis, also done on my Mac.

The Arduino had no problem continuously playing sounds, chasing the LEDs, and reacting to an input while also driving a PWM output for the gauge.

Wow! Nice work! Beats anything from the store by a mile.

Awesome!

Wow! Great costume! Soooo much better than the Buzz Lightyear my son wore this year! Going to have to start planning early this year, Thanks for the awesome inspiration

I appreciate all the compliments, everyone!

Since I posted the code over at the adafruit forum, I'll post it here as well. I'm not normally a programmer, so this is the result of what I've learned around here. Comments are welcome, but be gentle. :slight_smile:

/* 
This is a sketch for a robot costume for a little kid. 
Jerry Adlersfluegel
10/2009

Using Arduino 17, on an Arduino Diecimila with ATmega328

Costume features:

Chaser LEDs surrounding the Aruino. People will want to know how it all works,
so we want to show off the fact that there's a computer in there. Will be
mounted on the back of the costume, so this will also be a visibility safety
feature.

Candy input slot / Analog gauge. There is an electric eye in the input slot
(with a .75 sec debounce/guard time), and the gauge indicates that candy has been
received, climbing higher with each piece inserted, until the desired amount of
candy has been exceeded, at which point the giver will be rewarded with the
gauge going crazy! (That is, the needle will swing back and forth wildly.) Then,
after an amount of time, the gauge will reset, so the needle is back to 0 at the
next house. This time is enough to get a laugh without showing the giver that it
resets, but not so long that the next house sees the needle still going crazy.

Sound effects via Adafruit Wave Shield. There's an idle loop playing robot
beeps, which gets interrupted by candy input. When the candy count increases,
there is a synthesized speech recording that will be played, a different one for
each quantity. When playback of one of those files completes, the idle loop
starts again.

[Still need to document the circuits.]
*/

// scheduler library http://www.arduino.cc/playground/Code/Scheduler
#include <Scheduler.h>
// LED library http://www.arduino.cc/playground/Code/LED
#include <LED.h>
// Button library http://www.arduino.cc/playground/Code/Button
#include <Button.h>
// library for doing things periodically http://www.arduino.cc/playground/Code/TimedAction
#include <TimedAction.h>
// libraries and includes for wave shield
#include <AF_Wave.h>
#include <avr/pgmspace.h>
#include "util.h"
#include "wave.h"


// stuff for wave shield
AF_Wave card;
File f;
Wavefile wave;      // only one!

// stuff for the analog gauge
const int GaugePin = 6; // pwm pin for driving analog gauge ** found out you can't use pin 9 for pwm w/wave shield!!
// create an LED object for the gauge, since we're using it like an LED, kinda sorta
LED Gauge = LED(GaugePin); // also, this object removes the need to put anything for it in the setup()
TimedAction GaugeReward = TimedAction(250,CandyOverloadIndication); // to set interval for toggling the gauge

// stuff for the candy input sensor
const int CandyInputPin = 7; // pin for the input of the IR sensor - SEN-00241 from sparkfun
// create a button object for this input. 
Button CandyInput = Button(CandyInputPin, PULLDOWN); // again, no setup() required
int CandyCount = 0; 
boolean CandyInputBlocked = false; // used to prevent one piece from being overcounted, sort of a debounce
const int CandyGoal = 3; // how much candy to beg for at each house; number of steps on the gauge
boolean CandyIsOverloaded = false; // used in indicating if extra candy was collected
// scheduler object used to reset the candy counter after an amount of time, so it's zeroed at the next house
Scheduler CandyCountResetTime = Scheduler();
Scheduler CandyInputDebounce = Scheduler();

// stuff for the LED chasers
const byte NumberOfChasers = 6;
const byte ChaserInterval = 1000;
LED Chaser[NumberOfChasers] = { 
  LED(14), LED(15), LED(16), LED(17), LED(18), LED(19) }; // using the analog inputs as digital outs
byte CurrentChaserLED = 0; // keep track of which chaser LED is current
TimedAction ChaserAction = TimedAction(ChaserInterval, ChaserUpdate); // create protothread for running the chaser

void setup()
{
  Serial.begin(9600);
  // wave shield pins
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);


  if (!card.init_card()) {
    putstring_nl("Card init. failed!"); 
    return;
  }
  if (!card.open_partition()) {
    putstring_nl("No partition!"); 
    return;
  }
  if (!card.open_filesys()) {
    putstring_nl("Couldn't open filesys"); 
    return;
  }
  if (!card.open_rootdir()) {
    putstring_nl("Couldn't open dir"); 
    return;
  }
  //
  //  putstring_nl("Files found:");
  //  ls();


  // initalize the chaser LEDs. We want all on except the first one
  //
  // turn each Chaser LED
  for(int i=0; i < NumberOfChasers; i++) 
  { 
    Chaser[i].on();
  }
  // and then flip the first one
  Chaser[CurrentChaserLED].toggle();

  playfile("0IDLE.WAV");

}

void loop()
{
  //  Serial.println(CandyCount);
  ReadSensors();
  UpdateGauge();
  GaugeReward.check();
  ChaserAction.check();
  CandyCountResetTime.update();


  if(!wave.isplaying)
  {
    card.close_file(f);
    playfile("0IDLE.WAV"); 
  }
}


// Read the values of all sensors, and set variables that reflect their states.
void ReadSensors()
{
  // check if the candy sensor sees candy now, and didn't last time. If so, increment the candy count
  if (CandyInput.isPressed() && CandyInput.stateChanged())
  {
    CandyInputDebounce.update();
    if (!CandyInputBlocked) // during the debounce period, don't do anything with this input
    {
      CandyCount++;
      // also, schedule a time to reset CandyCount to 0, after clearing the schedule queue
      CandyCountResetTime.clear(); // clear the Scheduler queue
      CandyCountResetTime.schedule(ResetCandyCount, 20000); // 20 sec in milliseconds
      CandyInputBlocked = true; // to prevent input from being read
      CandyInputDebounce.schedule(ResetCandyInputDebounce, 750); // schedule a .75 second debounce of candy input
      // and finally, play a sound associated with this qunatity of candy
      switch (CandyCount)
      {
      case 1: 
        playfile("1CANDY.WAV");
        break;
      case 2:
        playfile("2YUM.WAV");
        break;
      case 3: 
        playfile("3THANKS.WAV");
        break;
      case 4:
        playfile("4OVER.WAV");
        break;
      default:
        break;
      }
    }
  }
}

void playfile(char *name) 
{
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
    card.close_file(f);
  }
  f = card.open_file(name);
  if (!f) {
    putstring("Couldn't open file "); 
    Serial.print(name); 
    return;
  }
  if (!wave.create(f)) {
    putstring_nl("Not a valid WAV"); 
    return;
  }
  // ok time to play!
  wave.play();
}

// set the value the gauge displays
void UpdateGauge()
{
  if (CandyCount <= CandyGoal) 
  {
    // set the gauge to the progress towards the candy goal. map() that to the PWM range 0-255
    Gauge.setValue(map(CandyCount, 0, CandyGoal, 0, 255));
    CandyIsOverloaded = false;
  }
  else
  {
    CandyIsOverloaded = true;
  }
}

void CandyOverloadIndication()
{
  if (CandyIsOverloaded)
  {
    Gauge.toggle(); 
  }
}

// fuction to reset the candy input counter
void ResetCandyCount()
{
  CandyCount = 0;
  CandyIsOverloaded = false;
}

// function to flip the current chaser, increment the current value, then flip the next LED
void ChaserUpdate()
{
  Chaser[CurrentChaserLED].toggle();
  CurrentChaserLED == NumberOfChasers - 1 ? CurrentChaserLED = 0 : CurrentChaserLED++;
  Chaser[CurrentChaserLED].toggle();
}

// function to clear the candy input blocked flag
void ResetCandyInputDebounce()
{
 CandyInputBlocked = false; 
}