Lab 6 Writeup Thomas Carney


Thomas Carney

 

Lab 6 Writeup

 

Introduction:

We learned how to use the TRS connector and wired it up to our decoder; however, the TRS connector often came loose from the breadboard. For the final project the TRS connector will have to be soldered to a board. We wired up the decoder and got the sine wave test to play as well as the soundtrack. We could not deduce what soundtrack was playing although it had something to do with chocolate and yum-yum. We built a barebones MP3 player and installed a interrupt with a hardware debouncer. After trial and error we found the 100 nF capacitor and 10 kΩ to work well as a hardware debouncer. A video of our MP3 player can be found here: http://www.youtube.com/watch?v=w8jiU39g5uI

 

Part A.

The TRS connector was marked with different wiring and connected to our MP3 decoder.

 

Part B. Connect And Test Your Decoder

Describe the tone pattern you hear.

It cycles through 3 tones pitches each one second long. There is a high tone, followed by a medium tone, and finally a low tone.

 

Part C. Play Some Music!

Tell a member of the teaching team what your song is (or better yet, play it for us). If you don't know the name, just decipher some of the lyrics.

It appears to be some sort of song about chocolate and yum yum. There is also a laugh track. No one in the Wednesday night lab could identify the song, and it appears that everyone had the same song on their card.

 

How would you alter the code to play a second song? How does it keep from playing the text file?

If a second song file was in the directory, then we would not have to modify the code because the code attempts to play all songs in the main directory. If we wanted to repeat the playing of the songs, we could also put the play function in the main loop loop so that it constantly plays. We could also manually call the mp3 play function and pass it the name of the mp3 file. The function does not play a text file because we first check the extension of the file to see if it is an mp3 or wav

 

Change the code to save the current volume setting in your EEPROM and fetch and set that value during initialization.

Matt told us that he used first 500 EEPROM for part of his library so we decided to save the volume setting at 999. We placed the following line of code at the end of the setup to write the current volume setting to EEPROM, the volume can range from 0-256, thus one byte of storage is necessary.

 

End of setup:

EEPROM.write(999, 220);

 

At the beginning of the setup function we read the value from memory, first initializing it to a default value.

 

Beginning of setup:

unsigned char mp3_vol=240;

mp3_vol=EEPROM.read(999);

 

Part D. Pause to Learn About Interrupts

We found that our circuit experienced bouncing. When we pressed the button once the arudino registered it as multiple hits. Usually it registered around 4 hits. We attempted to remedy this problem by building a low pass filter. After trial and error we found the 100 nF capacitor and 10 kΩ to work well as a hardware debouncer. A video of our MP3 player can be found here: http://www.youtube.com/watch?v=w8jiU39g5uI

 

Draw us a quick sketch of what your circuit looks like.

 

 

 

Barebones MP3 Player Code

/*
 * example sketch to play audio file(s) in a directory using the mp3
 * library for playback and the arduino fatfs wrapper to read files.
 * based on frank zhao's player at http://frank.circleofcurrent.com/
 *
 * (c) 2010 david sirkin sirkin@stanford.edu
 *
 * Heavily reworked by Matthew Seal (mattseal@stanford.edu)
 *
 */
 
// Use 40*13 = 520 bytes of EEPROM
#define MAX_FILE_COUNT 40
#define MAX_NAME_LENGTH 13

#include <mp3.h>
#include <mp3conf.h>

// include the SD library:
#include <SD.h>
#include <EEPROM.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

#define DEBUG // Comment this line to remove debugging features

 

#define mp3_cs      5             // 'command chip select' to cs pin
#define sd_cs       4             // 'chip select' for SD card

#define dcs         14            // (Pin A0) 'data chip select' to bsync pin
#define rst         6             // 'reset' to decoder's reset pin
#define dreq        15            // (Pin A1) 'data request line' to dreq pin

#define read_buffer 256           // size of the microsd read buffer
unsigned char mp3_vol=240;           // output volume range is 0 to 254
volatile boolean paused=false;

/* This function places the current value of the heap and stack pointers in the
 * variables. You can call it from any place in your code and save the data for
 * outputting or displaying later. This allows you to check at different parts of
 * your program flow.
 * The stack pointer starts at the top of RAM and grows downwards. The heap pointer
 * starts just above the static variables etc. and grows upwards. SP should always
 * be larger than HP or you'll be in big trouble! The smaller the gap, the more
 * careful you need to be. Julian Gall 6-Feb-2009.
 */
uint8_t *heapptr, *stackptr;
uint16_t diff=0;
void check_mem() {
  stackptr = (uint8_t *)malloc(4);          // use stackptr temporarily
  heapptr = stackptr;                     // save value of heap pointer
  free(stackptr);      // free up the memory again (sets stackptr to 0)
  stackptr =  (uint8_t *)(SP);           // save value of stack pointer
}


/* Stack and heap memory collision detector from: http://forum.pololu.com/viewtopic.php?f=10&t=989&view=unread#p4218
 * (found this link and good discussion from: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720%3Bstart=all )
 * The idea is that you need to subtract your current stack pointer (conveniently given by the address of a local variable)
 * from a pointer to the top of the static variable memory (__bss_end). If malloc() is being used, the top of the heap
 * (__brkval) needs to be used instead. In a simple test, this function seemed to do the job, showing memory gradually
 * being used up until, with around 29 bytes free, the program started behaving erratically.
 */
extern int __bss_end;
extern void *__brkval;

int get_free_memory()
{
  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;
}

/*
 * Adopted from the ladyada.net tutorial on Ethernet protocol
 * @http://www.ladyada.net/learn/arduino/ethfiles.html
 *
 * Lists all files from a directory:
 * ListFiles(SdFile *dir, char *doubleBuf, int maxCount)
 *
 * dir is the directory to list
 * doubleBuf is the buffer to store the results in -- this should be fileNames to start
 * maxCount is the size of the buffer.
 *
 * returns the number of file names written into the doubleBuf array
 */
int ListFiles(SdFile *dir) {
  int count = 0;
  dir_t p;
 
  dir->rewind();
  while (dir->readDir(&p) > 0 && count < MAX_FILE_COUNT) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;

    /* Uncomment to allow subdirectories to be listed (with a '/' after the name) */
    // only list subdirectories and files
    //if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
   
    /* Uncomment to only allow files to be listed */
    // only list files
    if (!DIR_IS_FILE(&p)) continue;

    // print file name into string
    uint8_t pos = 0;
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] != ' ') {
        EEPROM.write((count*MAX_NAME_LENGTH)+pos, p.name[i]);
        pos++;
      }
    }
   
    // append slash if file is a directory
    if (DIR_IS_SUBDIR(&p)) {
      EEPROM.write((count*MAX_NAME_LENGTH)+pos, '/');
      pos++;
    }
   
    // add the end string character
    EEPROM.write((count*MAX_NAME_LENGTH)+pos, '\0');
    count++;
  }
#ifdef DEBUG
  Serial.print("Stored ");
  Serial.print(count+1);
  Serial.print(" files, using ");
  Serial.print((count+1)*MAX_NAME_LENGTH, DEC);
  Serial.println(" byes in EEPROM.");
#endif
  return count+1;
}

/*
 * read in buffer 'bytes' of 'read_buffer' size from the file opened
 * in the while loop below. This function assumes that file has already
 * been opened and does NOT close the file. This means you need to do this
 * outside of the function.
 */
void mp3_play (SdFile *file) {
  unsigned char bytes[read_buffer]; // buffer to send to the decoder
  unsigned int bytes_to_read;       // number of bytes to read from sd card
  // reset the file to be at the beginning of data
  file->seekSet(0);
  // Try to read 'read_buffer' length of bytes and send it to the decoder.
  // If less than 'read_buffer' bytes are available, stop last send.
  do {
    if(!paused) {
    bytes_to_read = file->read(bytes, read_buffer);
    Mp3.play(bytes, bytes_to_read);
    //Serial.println('.');
    }
  }
  while (bytes_to_read == read_buffer);
#ifdef DEBUG
  Serial.println("Played song...");
#endif
}

/*
 * play back an mp3 or wav file (only!) in the root directory. first
 * check that it's a file (and not a directory); next, check that it
 * has a proper extension; finally, play only if it opens cleanly.
 */
void dir_play (SdFile *dir) {
  int numFiles = ListFiles(dir);
  for (int i = 0; i < numFiles; i++) {
    char fn[MAX_NAME_LENGTH+2];
    int fnln = MAX_NAME_LENGTH;
    // get file name and name length
    for (int j = 0; j < MAX_NAME_LENGTH; j++) {
      fn[j] = EEPROM.read((i*MAX_NAME_LENGTH)+j);
      // end of name
      if (fn[j] == '\0') {
        fnln = j;
        j = MAX_NAME_LENGTH;
      }
      // directory => nullify entry name
      else if (fn[j] == '/') {
        fn[0] = '\0';
        fnln = 0;
        j = MAX_NAME_LENGTH;
      }
    }
    if (fnln > 4) {
      fn[fnln+1] = '\0';
      fn[fnln] = fn[fnln-1];
      fn[fnln-1] = fn[fnln-2];
      fn[fnln-2] = fn[fnln-3];
      fn[fnln-3] = '.';
      fnln++;
#ifdef DEBUG
      Serial.print("Opening ");
      Serial.println(fn);
#endif
    }
    SdFile dataFile;
    // ensure we can open the file
    if (dataFile.open(dir, fn, O_RDONLY) > 0) {
      // ensure it's not a directory
      if (!dataFile.isDir() || fn == 0) {
        //get filenames in directory fn
        //get length of the filename fnln
       
#ifdef DEBUG
        Serial.print(fn);
        Serial.println(": File is valid.");
#endif
 
        if ((fn[fnln-3] == 'M' && fn[fnln-2] == 'P' && fn[fnln-1] == '3') ||
   (fn[fnln-3] == 'W' && fn[fnln-2] == 'A' && fn[fnln-1] == 'V')) {
     
#ifdef DEBUG
          Serial.print("Playing ");
          Serial.println(fn);
#endif
     //buffer data for playing
     mp3_play(&dataFile);
        }
#ifdef DEBUG
        else {
          Serial.print("Skipping ");
     Serial.println(fn);
        }
#endif
      }
#ifdef DEBUG
      else {
        Serial.print("File is directory ");
 Serial.println(fn);
      }
#endif
    }
    else {
      Serial.print("File is not valid ");
      Serial.println(fn);
    }
  }
}

 

/*
 * initialize the processor speed, setup the fatfs sd card (or, mms)
 * filesystem, setup mp3 playback and register the pins used (device
 * specific configuration)
 */
void setup() {
  Serial.begin(9600);            // initialize the serial terminal
 
  pinMode(10, OUTPUT);     // change this to 53 on a mega
  digitalWrite(10, HIGH);
 
   mp3_vol=EEPROM.read(999);
   attachInterrupt(0,pauseSong,FALLING);
  
   Serial.print("MP3 Volume: ");
   Serial.println(mp3_vol,DEC);
  
   // see if the card is present and can be initialized:
  if (!SD.begin(sd_cs)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
 
  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, sd_cs)) {
    Serial.println("Initialization failed. Things to check:");
    Serial.println("* is a card is inserted?");
    Serial.println("* Is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  }
 
  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }
 
  if (!root.openRoot(&volume)) {
    Serial.println("Failed to open root");
    // don't do anything more:
    return;
  }
 
  Mp3.begin(mp3_cs,dcs,rst,dreq);  // decoder cs, dcs, rst, dreq pin
  Mp3.volume(mp3_vol);             // default volume level is silent
 
#ifdef DEBUG
  Serial.println("Card initialized.");
  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
 
  Serial.println("Checking free memory...");
  Serial.print("There are ");
  Serial.print(get_free_memory(), DEC);
  Serial.println(" bytes of free memory.");
  Serial.print("~");
  Serial.print(read_buffer, DEC);
  Serial.println(" free bytes required for mp3 playing.");
#endif

  // List all files to in the root directory and play each
  // one at a time. Maximum of MAX_FILE_COUNT files will be read
  dir_play(&root);
  root.close();
  EEPROM.write(999, 220);
}

void pauseSong() {
  Serial.println("Button pressed");
  paused=!(paused);
}

// Do nothing for now
void loop() {
  while (1);
}