Annie Scalmanini Lab 6


and by "Lab 6," I mean "Final Project page"

 

 

 

 

 

 

 

 

 

mpTree Code:

 

  /*

   * The setup: a barebones mp3 player on a perf board using level shifters on shared SPI pins (11, 12, and 13) and IR sensors on analog pins (A5      and A6 to start)

     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

  #define mp3_vol     254           // output volume range is 0 to 254

 

  int lcdWidth = 84;

  int lcdHeight = 6;

 

  int sensor5 = A5;

  int sensor6 = A6;

 

  byte valIR5 = 0;

  byte valIR6 = 0;

  int statePin5 = LOW;

  int statePin6 = LOW;

 

  int THRESHOLD = 255; 

 

  //int THRESHOLD = 78; //<--I'll have to change this during calibration... do i include a part during setup when the RESET button is pressed on the Arduino to change this value?? (so does this mean that the threshold is 100 mV?)

  //

 

  /* 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 {

      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;

          break;

        } 

        // directory => nullify entry name

        else if (fn[j] == '/') {

          fn[0] = '\0';

          fnln = 0;

          j = MAX_NAME_LENGTH;

          break;

        }

      }

      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

    pinMode(sensor5, OUTPUT); 

    pinMode(sensor6, OUTPUT);

    digitalWrite(10, HIGH);

 

    // 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

 

  }

 

 

  void loop() {

 

   //note: IR values: min = 19, max = 3457, threshold = 78 (for now)

   valIR5 = analogRead(sensor5);

   valIR6 = analogRead(sensor6);

 

   while (valIR6 >= THRESHOLD || valIR5 >= THRESHOLD) {

      // 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();

    }

  } 

 

 

  /*INSERT CODE FOR SENSORS*/

     //**note: should the part of the code that checks for "inactivity" from the IR sensors be included here in "dir_play" or in "mp3_play"?

 

      /*need to first introduce IR sensors during setup - perhaps on A5 and A6?

       *Should I include a delay after mp3_play(&dataFile)?

       */

 

  /*CODE FROM ARDUINO EXAMPLE, good loop for detecting sensor values */

 

              ///* Adapated from Arduino tutorial for Knock Sensor

              //   http://www.arduino.cc/en/Tutorial/KnockSensor 

              //   (cleft) 2005 David Cuartielles for K3

              // * ----------------

              // *

              // * Program using IR sensors to trigger

              // *

              // * We have to listen to analog pins and detect 

              // * if the signal goes over a certain threshold. If so, it writes

              // * "I'm turned on!" to the serial port if the Threshold is crossed,

              // * and turns on the mpTree device.

              // 

              // Running this example requires you have an Arduino board

              // as peripheral hardware sending values and adding an EOLN + CR 

              // in the end. More information can be found on the Arduino 

              // pages: http://www.arduino.cc

 

              // //note: LCD screen goes from x = 0-83, y = 0-5

 

              //int lcdWidth = 84;

              //int lcdHeight = 6;

              //string welcomeMessage = "I'm turned on!"

 

              //int sensor5 = A5;

              //int sensor6 = A6;

              //

              //byte valIR5 = 0;

              //byte valIR6 = 0;

              //int statePin5 = LOW;

              //int statePin6 = LOW;

 

              //int THRESHOLD = 78; //<--I'll have to change this during calibration... do i include a part during setup when the RESET button is pressed on the Arduino to change this value?? (so does this mean that the threshold is 100 mV?)

              //

                  //void setup() {

                  // pinMode(sensor5, OUTPUT); 

                  // pinMode(sensor6, OUTPUT);

 

                  // beginSerial(9600);

                  //}

              //

                  //void loop() {

                  //  valIR5 = analogRead(sensor5);     

                  //  if (valIR5 >= THRESHOLD) {

                  //    statePin5 = !statePin5;

                  //    powerUpTheTree();

                  //    digitalWrite(ledPin, statePin5); // <-- THIS IS WHERE I NEED TO CHANGE 'ledPin' to whatever pin turns the device on!!! Would that be CS for the decoder?

                  //    printByte(10);

                  //    printByte(13);

                  //  }

                  // valIR6 = analogRead(sensor6);

                  // if (valIR6 >= THRESHOLD) {

                  //   statePin6 = !statePin6; 

                  //   //call the method PLAY_SONG!

                  //   digitalWrite(ledPin, statePin6); // <-- THIS IS WHERE I NEED TO CHANGE 'ledPin' to whatever pin turns the device on!!! Would that be CS for the decoder?

                  //   delay(100);  // we have to make a delay to avoid overloading the serial port

                  //}

 

 

 

 

**Detailed photos, videos, and instructions to come - everything had to get disassembled and packed until after graduation :-/

 

 

 

 

more extensions for the summer:

-different sensors trigger different music

-music stops playing or volume adjusts if motion stops or people walk away

-use the op-amp to wire up speakers instead of audio jack

-leaf cases for the sensors and speakers

-add buttons, control functionality

-code for the LCD screen

-heat-shrink wrap the IR sensor wires  

-calibrate the sensors when the reset button is pushed