Instructables

Toothless, the Roaring Plush Puppet

Featured
Picture of Toothless, the Roaring Plush Puppet
This is the second project of the Spring 2012 Things That Think computer science course at the University of Colorado, Boulder. The goal of this project was to create a plush that also used computing in some way. The team, comprised of Jed Diller and Ken Hoff, decided to do a dragon puppet that would sense when its mouth was open and roar with the volume varying depending on the extent to which the mouth was open. The eyes also change color, from blue to red, as the mouth is opened wider - indicating the dragon is getting mad. The lovable Toothless from How To Train Your Dragon was the dragon we modeled our puppet after. The behavior and final product can be seen below.

  

You may notice in the video that one of the openings of the mouth was not accompanied by a roar. This happened because the mouth was not fully closed to reset the behavior between roars.

 
Remove these adsRemove these ads by Signing Up

Step 1: What You'll Need

Picture of What You'll Need
mp3Shield.jpg
microSDcard.jpg
40-PIN-Breakaway-Header-Connector.jpg
sensor-photocell.jpg
rgbLED.jpg
1 Arduino UNO

1 SparkFun MP3 Shield (http://www.sparkfun.com/products/10628)

1 MicroSD card (and some external way to transfer data to and from the card)

2 6-pin headers and 2 8-pin headers (for connecting the shield to the arduino

1 Light sensor

1 resistor (any old kilo Ohm resistor should do)

2 RGB LEDs

Single core wire

Step 2: Electronics Overview

Picture of Electronics Overview
Light sensor

Connect +5V to light sensor, to resistor, then to ground
Connect sensor line between light sensor and resistor (not after resistor!)

MP3 Shield

Getting your MP3 shield working: http://www.sparkfun.com/tutorials/295
Solder headers on carefully to the shield, then attach it to the UNO board by simply plugging it in
Plug in speakers or headphones to headphone jack

LEDs

Wire 2 tricolor (RGB) LED's in parallel, about 6 inches apart
Connect the wire that runs to the blue pins of the LED's to pin 5 on the MP3 shield
Connect the wire that runs to the red pins of the LED's to pin 10 on the MP3 shield

Step 3: Behavioral Code Overview

In the main loop, we read continuously from the light sensor
If the light sensor input goes above a certain value, then the MP3 starts to play
If the light sensor input goes below a certain value, then the MP3 is stopped

While the MP3 is playing, the light sensor input is converted into a decibel level for the MP3, where more light equals louder sound
In addition, the light sensor input controls the LED values inside the main loop and the MP3 loop
As the light sensor input increases, the red LED increases and the blue LED decreases


Here is the code used:

<code>

#include <SPI.h>

#include <SdFat.h>
#include <SdFatUtil.h>

#define TRUE 1
#define FALSE 0

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile track;

//MP3 Player Shield pin mapping. See the schematic
#define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for more data
#define MP3_RESET 8 //Reset is active low
//Remember you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the SD card.

//VS10xx SCI Registers
#define SCI_MODE 0x00
#define SCI_STATUS 0x01
#define SCI_BASS 0x02
#define SCI_CLOCKF 0x03
#define SCI_DECODE_TIME 0x04
#define SCI_AUDATA 0x05
#define SCI_WRAM 0x06
#define SCI_WRAMADDR 0x07
#define SCI_HDAT0 0x08
#define SCI_HDAT1 0x09
#define SCI_AIADDR 0x0A
#define SCI_VOL 0x0B
#define SCI_AICTRL0 0x0C
#define SCI_AICTRL1 0x0D
#define SCI_AICTRL2 0x0E
#define SCI_AICTRL3 0x0F

//This is the name of the file on the microSD card you would like to play
//Stick with normal 8.3 nomeclature. All lower-case works well.
//Note: you must name the tracks on the SD card with 001, 002, 003, etc.
//For example, the code is expecting to play 'track002.mp3', not track2.mp3.
char trackName[] = "sound.mp3";
int trackNumber = 1;
int previousTrigger = 1; //This indicates that we've already triggered on 1

char errorMsg[100]; //This is a generic array used for sprintf of error messages

int inputPin = A0;
int inputValue = 0;
int blueOutputValue = 0;
int blueOutputLED = 5;
int redOutputValue = 0;
int redOutputLED = 10;
int outputVolume = 0;

long lastCheck; //This stores the last millisecond since we had a trigger

int is_playing;
int time_since_play;

int checkTriggers(void) {

#define DEBOUNCE  100

  int foundTrigger = 255;

  //Once a trigger is activated, we don't want to trigger on it perpetually
  //But after 3 seconds, reset the previous trigger number
  if( (previousTrigger != 255) && (millis() - lastCheck) > 3000) {
    lastCheck = millis();
    previousTrigger = 255;
    Serial.println("Previous trigger reset");
  }

  if(foundTrigger != previousTrigger){ //We've got a new trigger!
    previousTrigger = foundTrigger;

    Serial.print("T");
    Serial.println(foundTrigger, DEC);

    return(foundTrigger);
  }
  else
    return(255); //No triggers pulled low (activated)
}

// playMP3 - included function from SparkFun example

void playMP3(char* fileName) {

  if (!track.open(&root, fileName, O_READ)) { //Open the file in read mode.
    sprintf(errorMsg, "Failed to open %s", fileName);
    Serial.println(errorMsg);
    return;
  }

  sprintf(errorMsg, "Playing track %s", fileName);
  Serial.println(errorMsg);

  uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. VS1053 can take 32 bytes at a go.
  int need_data = TRUE;

  while(1) {
    while(!digitalRead(MP3_DREQ)) {
      //DREQ is low while the receive buffer is full
      //You can do something else here, the buffer of the MP3 is full and happy.
      //Maybe set the volume or test to see how much we can delay before we hear audible glitches

      //If the MP3 IC is happy, but we need to read new data from the SD, now is a great time to do so
      if(need_data == TRUE) {
        if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Try reading 32 new bytes of the song
          //Oh no! There is no data left to read!
          //Time to exit
          break;
        }
        need_data = FALSE;
      }

      //Check to see if we need to bail on this track
      if(checkTriggers() != 255) {
        Serial.println("Exiting MP3!");
        track.close(); //Close this track!
        previousTrigger = 255; //Trick the next check into thinking we haven't seen a previous trigger
        return;
      }
    }

    if(need_data == TRUE){ //This is here in case we haven't had any free time to load new data
      if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Go out to SD card and try reading 32 new bytes of the song
        //Oh no! There is no data left to read!
        //Time to exit
        break;
      }
      need_data = FALSE;
    }

    //Once DREQ is released (high) we now feed 32 bytes of data to the VS1053 from our SD read buffer
    digitalWrite(MP3_XDCS, LOW); //Select Data
    for(int y = 0 ; y < sizeof(mp3DataBuffer) ; y++)
      SPI.transfer(mp3DataBuffer[y]); // Send SPI byte

    digitalWrite(MP3_XDCS, HIGH); //Deselect Data
    need_data = TRUE; //We've just dumped 32 bytes into VS1053 so our SD read buffer is empty. Set flag so we go get more data

    inputValue = analogRead(inputPin);

    outputVolume = map(inputValue, 0, 1023, 60, -20); // <--- this is where you make it louder/softer depending on how much light input your puppet gets

    redOutputValue = map(inputValue, 0, 1023, 0, 255);
    blueOutputValue = map(inputValue, 0, 1023, 255, 0);

    Mp3SetVolume(outputVolume, outputVolume);

    analogWrite(redOutputLED, redOutputValue);
    analogWrite(blueOutputLED, blueOutputValue);

  }

  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating transfer is complete
  digitalWrite(MP3_XDCS, HIGH); //Deselect Data

  track.close(); //Close out this track

  sprintf(errorMsg, "Track %s done!", fileName);
  Serial.println(errorMsg);
}

void setup()
{
  pinMode(MP3_DREQ, INPUT);
  pinMode(MP3_XCS, OUTPUT);
  pinMode(MP3_XDCS, OUTPUT);
  pinMode(MP3_RESET, OUTPUT);

  digitalWrite(MP3_XCS, HIGH); //Deselect Control
  digitalWrite(MP3_XDCS, HIGH); //Deselect Data
  digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset

  pinMode(inputPin, INPUT);       // declare the LDR as an INPUT
  pinMode(redOutputLED, OUTPUT);  // declare the ledPin as an OUTPUT
  pinMode(blueOutputLED, OUTPUT);

  Serial.begin(57600); //Use serial for debugging
  Serial.println("MP3 Player Example using Control");

  //Setup SD card interface
  pinMode(10, OUTPUT);       //Pin 10 must be set as an output for the SD communication to work.
  if (!card.init(SPI_FULL_SPEED))  Serial.println("Error: Card init"); //Initialize the SD card and configure the I/O pins.
  if (!volume.init(&card)) Serial.println("Error: Volume ini"); //Initialize a volume on the SD card.
  if (!root.openRoot(&volume)) Serial.println("Error: Opening root"); //Open the root directory in the volume.

  //We have no need to setup SPI for VS1053 because this has already been done by the SDfatlib

  //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz.
  //Internal clock multiplier is 1.0x after power up.
  //Therefore, max SPI speed is 1.75MHz. We will use 1MHz to be safe.
  SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus speed to 1MHz (16MHz / 16 = 1MHz)
  SPI.transfer(0xFF); //Throw a dummy byte at the bus

  //Initialize VS1053 chip
  delay(10);
  digitalWrite(MP3_RESET, HIGH); //Bring up VS1053

  //Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD
  Mp3SetVolume(40, 40); //Set initial volume (20 = -10dB) Manageable
  //Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB) More quiet

  //Now that we have the VS1053 up and running, increase the internal clock multiplier and up our SPI rate
  Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier to 3.0x

  //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz.
  //Internal clock multiplier is now 3x.
  //Therefore, max SPI speed is 5MHz. 4MHz will be safe.
  SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed to 4MHz (16MHz / 4 = 4MHz)

  //MP3 IC setup complete

  Serial.println("Done with setup");

}

void loop()
{
  inputValue = analogRead(inputPin);

  //LED eyes bits (needs tweaking of values when placed in final puppet)

  redOutputValue = map(inputValue, 0, 1023, 0, 255);
  blueOutputValue = map(inputValue, 0, 1023, 255, 0);
  /* if (inputValue > 50)
   {
   blueOutputValue = 0;
   }
   if (inputValue < 20)
   {
   redOutputValue = 0;
   } */

  if (inputValue > 600) //if mouth is open enough
  {
    if (is_playing == 0)
    {
      playMP3(trackName);
      is_playing = 1;
    }
  }
  if (inputValue < 500) // if mouth is closed enough
  {
    track.close();
    is_playing = 0;

  }

  Serial.print(inputValue); // to see if your light sensor is working properly
  Serial.print("\n");
  analogWrite(redOutputLED, redOutputValue);
  analogWrite(blueOutputLED, blueOutputValue);
}



//Write to VS10xx register - from SparkFun example
//SCI: Data transfers are always 16bit. When a new SCI operation comes in
//DREQ goes low. We then have to wait for DREQ to go high again.
//XCS should be low for the full duration of operation.
void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte){
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(MP3_XCS, LOW); //Select control

  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x02); //Write instruction
  SPI.transfer(addressbyte);
  SPI.transfer(highbyte);
  SPI.transfer(lowbyte);
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete
  digitalWrite(MP3_XCS, HIGH); //Deselect Control
}



//Set VS10xx Volume Register - volume set function from SparkFun example
void Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){
  Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel);
}

</code>


Step 4: Puppet Planning

Picture of Puppet Planning
Obama_TTT_Toothless 025.JPG
Plans for the puppet started with measuring the dimension of the human arm. The rest of the puppet, meant to look like Toothless, was scaled around those arm dimensions.

Important dimension were:
- The length from your inner elbow to the center of your hand when flexed in to shadow puppet form
- The length from mid-hand to the end of the finger tips (this becomes the length of the upper half of the mouth, the thumb being the bottom half)
- The circumference of your arm (half of this gives the 2D width of the internal puppet)

Using these dimensions the interior sock puppet was made and the rest of the body was scaled around that. This took multiple iterations as seen in the image below. The scaling is a little tricky. It is recommended that you draw out what you want your puppet to look like, staring with the arm dimensions and then draw the rest free form to look right and then measure what the component lengths actually are based on the scaling of the graph paper. In the second image it can be seen that the squares on the graph paper represented 4x4cm puppet dimensions.

This part is really free form. You can make your puppet as big or small as you want (a minimum given by your arm dimensions). We essentially came up with a 2D view of the puppet that could then be stuffed to become 3D.
 
With the puppet planned out, the next step was to cut fabric to the planned dimensions and start sewing!

Step 5: Puppet Assembly

The cutting and assemble process was pretty straight forward, just time consuming. The shapes determined from the puppet plan were drawn out to full scale on the back side of the puppet fabric and cut out. The ensure pieces that had to go together were near identical, the first shape cut out was used to trace out the other pieces.

While things like the feet and body were rather simple, connecting the internal puppet to the body and head was a little tricky. To disguise the internal puppet (made of white felt), the pockets (where the operators fingers go) had to be made of the dragon skin pleather, as did the parts that attached to the upper and lower halves of the head to the internal puppet. The internal puppet sleeve attached to the body at a slit in the belly. The making of the internal puppet can be seen in the images below.

The tail was given some internal structure using strands of nylon wire glued to one side of the fabric. 

Here is the total piece count:
- Legs x 8 (4 legs with mirrored tops a bottoms, the same pieces was used as a trace of all by flipping it over between top and bottom halves)
- Tail x 2
- Body x 2
- Head x 4 (same shape for upper and lower halves of the head. In hindsight the lower half should have been much smaller)
- Internal puppet sleeve x 2
- Internal puppet finger pouch x 2
- Internal puppet cover fabric and head mounting material x 2 
- Ears x 4

Step 6: Integration with Electronics

Picture of Integration with Electronics
Obama_TTT_Toothless 071.JPG
Obama_TTT_Toothless 073.JPG
Before the puppet was entirely sewn up, the eyes and light sensor had to be installed. The light senor was placed in the roof of the mouth and the eyes were placed in the upper half of the head. The wires from both of these ran from inside the top half of the head through the body (on the outside of the internal puppet) to the UNO and MP3 Shield in the back of the body. The UNO and MP3 Shield are made accessible in the back by a stretch of velcro on one side. This velcro also allows the USB and headphone cables to leave the puppet.

With the electronics sewn up, the puppet was complete.

Step 7: Final Product and Future Plans

Picture of Final Product and Future Plans
Below is a video of the final product. The sound is being output through some desktop computer speakers.



You may notice in the video that one of the openings of the mouth was not accompanied by a roar. This happened because the mouth was not fully closed to reset the behavior between roars.

Future plans:
Correcting the sound levels to proper logarithmic scale
Audio Processing (light sensor controlling low-pass filter instead of volume)
Refine head, add killer wings, and do internal stitching