Instructables

Lunch box that plays excerpts from "The Simpsons"

image2.jpeg
image3.jpeg
Though this is not a step by step instructable, I hope you can get the gist of what I did and I will try to answer any questions as best I can.



My son is a big fan of The Simpsons ( and I suppose, so am I).  I created this as a gift for him for Christmas.

What we have here is a Simpson's lunch box that , when the red switch is pressed, will play randomly one of 413 wav files that are stored on the SD card which is used by the Adafruit waveshield.

A video of it in use follows.


The materials used are:

- Arduino UNO
- Adafruit Wave Shield Kit v1.1
- 2GB SD card
- Simpsons lunch box - from Amazon.com
- 2 9 volt batteries and 2 holders - radio shack 
- 3 LEDs
- an 8 ohm speaker
- a DPDT switch
- a couple of capacitors , resistors and a LM386 chip for a small amplifier  as the sound out of the wave shield is not loud enough for the speaker.

- some wires


Also I used an SD card reader and a program called Switch-it to decompress the wav files .


Steps :
1.   bought a Arduino Uno -  many sources for this - about $25.00  


2.  Bought an Adafruit Waveshield Kit - about 22.00  .
This needs to be assembled and the instructions are here :
http://www.ladyada.net/make/waveshield/make.html 

(These are easy to follow instructions with many good pictures)


3. bought a 2gb SD card  to be used in the wave shield

4.  did a  internet  search for : "Simpsons wav" and found a couple of sites that had quite a good collection of wav file excerpts from the Simpsons.   I was able to(with aid of my son) download over 400 files and save them to the SD card.

- note the wave shield does not like compressed files so some of the files needed to be uncompressed which I did using a freeware software called "Switch-it" .


5. Since the sound output of the wave shield is OK for headphones but was not loud enough for the speaker, I have sent the output from the wave shield to a small LM386 amp that I built using the following instructions.

http://www.rason.org/Projects/icamps/icamps.htm


6 .  Added LEDs to indicate the power is on(side switch) and to indicate when it is ok to ask for a new file.

(note -  I added a feature which when one depresses the red switch when the green LED is blinking, all the wave files will be played)  

7. Some other observations :
-  basically the code waits for the red switch to be pressed. 
it then generates a randow number beween 1 and 413 ( the number of wave files) .  It then reads thru the wave files and increments a counter until the random number is reached.  It then plays that file.   This does mean the number of files is hard coded into the code.  Probably there is some way to have the code figure out how many files there are.
- sometimes it fails to play a file I think because it also does not like stereo files .
 

8. the code:

- copied from various sources and indicated so in the code.  Specifically,I started with code that is on the Adafruit site and modified as needed.  

/*

idea is to play a random wav file each time the button is press
// v2 now trying to add button and other code
// v2 works good
v3 - add ability to play all the songs by holding the button down    during the blinking

*/ 



#include
#include

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory





WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

uint8_t dirLevel; // indent level for file/dir names    (for prettyprinting)
dir_t dirBuf;     // buffer for directory reads


#define DEBOUNCE 5  // button debouncer
const int buttonPin = 8;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status

// for rsndom play
long randNumber;
long ct = 0;


// needed for playing all
  int playAll = 0;
int numPress  = 0;         // variable for reading the pushbutton status
int xx = 0;
long firstTime = 1;

// need to change this to equal numberr of wav files on sd card
long numWavs = 413;


  const int ledPin = 7;      // the number of the LED pin
// Variables will change:
//  int ledState = HIGH;             // ledState used to set the LED


/*
* Define macro to put error messages in flash memory
*/
#define error(msg) error_P(PSTR(msg))

// Function definitions (we define them here, but the code is below)
void play(FatReader &dir);

// this handy function will return the number of bytes currently free in RAM, great for debugging!  
int freeRam(void)
{
  extern int  __bss_end;
  extern int  *__brkval;
  int free_memory;
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
  }
  return free_memory;
}

//////////////////////////////////// SETUP
void setup() {
  // Serial.begin(9600);           // set up Serial library at 9600 bps for debugging

  // if analog input pin 0 is unconnected, random analog
  // noise will cause the call to randomSeed() to generate
  // different seed numbers each time the sketch runs.
  // randomSeed() will then shuffle the random function.
  randomSeed(analogRead(3));
// initialize the pushbutton pin as an input:
   pinMode(buttonPin, INPUT);    
// set the digital pin as output:


   pinMode(ledPin, OUTPUT);     
  //    digitalWrite(ledPin,HIGH);
 
  putstring_nl("\nWave test!");  // say we woke up!
 
  putstring("Free RAM: ");       // This can help with debugging, running out of RAM is bad
//  Serial.println(FreeRam());


  if (!card.init()) {         //play with 8 MHz spi (default faster!) 
    error("Card init. failed!");  // Something went wrong, lets print out why
  }
 
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
 
  // Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
    if (vol.init(card, part))
      break;                           // we found one, lets bail
  }
  if (part == 5) {                     // if we ended up not finding one  :(
    error("No valid FAT partition!");  // Something went wrong, lets print out why
  }
 
  // Lets tell the user about what we found
  putstring("Using partition ");
//   Serial.print(part, DEC);
  putstring(", type is FAT");
//   Serial.println(vol.fatType(), DEC);     // FAT16 or FAT32?
 
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    error("Can't open root dir!");      // Something went wrong,
  }
 
  // Whew! We got past the tough parts.
  putstring_nl("Files found (* = fragmented):");

  // Print out all of the files in all the directories.
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
 
 
//   digitalWrite(ledPin,HIGH);
}


//////////////////////////////////// LOOP
void loop() {
  // short loop for 3 seconds tyhe first time only
  // if button pressed more than once, p[lay all waves
// Serial.println( firstTime);
//  delay(1000);
   if (firstTime == 1){
  do
{
   digitalWrite(ledPin,HIGH);
   delay(250);
    digitalWrite(ledPin,LOW);
    delay(250);
   
  numPress  = digitalRead(buttonPin);
   if (numPress == HIGH) {
     playAll++;
  //   firstTime ++;
   }    
  xx++;
// Serial.println( xx);
} while (xx < 10);

firstTime ++;
   }
 
   Serial.println( firstTime);
   Serial.println( playAll);
    delay(1000);
 
 
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
   digitalWrite(ledPin,HIGH);
  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (buttonState == HIGH) {

ct = 0;    
    // play a wav file  ie continue with the code  
   
    digitalWrite(ledPin,LOW);

 
  // print a random number from 1 to 60
  randNumber = random(1, numWavs);
//   Serial.println(randNumber);

  delay(50);
 
 
  root.rewind();
  play(root);
  }
  else {
    // do nothing
   
  }
}

/////////////////////////////////// HELPERS
/*
* print error message and halt
*/
void error_P(const char *str) {
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while(1);
}
/*
* print error message and halt if SD I/O error, great for debugging!
*/
void sdErrorCheck(void) {
  if (!card.errorCode()) return;
  PgmPrint("\r\nSD I/O error: ");
//  Serial.print(card.errorCode(), HEX);
  PgmPrint(", ");
//  Serial.println(card.errorData(), HEX);
  while(1);
}
/*
* play recursively - possible stack overflow if subdirectories too nested
*/
void play(FatReader &dir) {
  FatReader file;
  while (dir.readDir(dirBuf) > 0) {    // Read every file in the directory one at a time
 
    // Skip it if not a subdirectory and not a .WAV file
    if (!DIR_IS_SUBDIR(dirBuf)
         && strncmp_P((char *)&dirBuf.name[8], PSTR("WAV"), 3)) {
      continue;
    }

  //  Serial.println();            // clear out a new line
   
    for (uint8_t i = 0; i < dirLevel; i++) {
    //   Serial.write(' ');       // this is for prettyprinting, put spaces in front
    }
    if (!file.open(vol, dirBuf)) {        // open the file in the directory
      error("file.open failed");          // something went wrong
    }
   
    if (file.isDir()) {                   // check if we opened a new directory
      putstring("Subdir: ");
      printEntryName(dirBuf);
//     Serial.println();
      dirLevel += 2;                      // add more spaces
      // play files in subdirectory
      play(file);                         // recursive!
      dirLevel -= 2;   
    }
    else {
      // Aha! we found a file that isnt a directory
      putstring("Playing ");
      printEntryName(dirBuf);              // print it out
      if (!wave.create(file)) {            // Figure out, is it a WAV proper?
        putstring(" Not a valid WAV");     // ok skip it
      } else {
        Serial.println(); 


        // Hooray it IS a WAV proper!
   ct++;
  //   Serial.println(randNumber);  
   //   Serial.println(ct);  
     // here is where we play the file
     // however only want the one wav file which is selected by random number
     if(( randNumber == ct) || (playAll > 1)){
      
        wave.play(); 
       
         // make some noise!
       
        uint8_t n = 0;
        while (wave.isplaying) {// playing occurs in interrupts, so we print dots in realtime
          putstring(".");
          if (!(++n % 32))Serial.println();
          delay(100);
        }
           digitalWrite(ledPin,HIGH);
     }

        sdErrorCheck();                    // everything OK?
        // if (wave.errors)Serial.println(wave.errors);     // wave decoding errors
      }
    }
   
      
  }
}