Sparkfun MP3 Shield Arduino Library

Posted in Arduino Libraries by Bill
28 Jan 2012

The Sparkfun MP3 Player Shield for Arduino is a inexpensive and easy way to add MP3 playback capability to your Arduino project. But it was lacking an easy to use Arduino Library to go along, so I fixed that. Introducing the SFEMP3Shield library just for this shield. Now playing an MP3 files is as easy as MP3player.playTrack(5); and all the work is done behind the scenes.

Most of this library comes from the existing example code, with the major new feature of being interrupt driven. So no need to worry about feeding the MP3 chip, whenever it’s ready for more the library will be ready to feed it. This allows the Arduino to perform other tasks while music is playing. The most basic use of the library is as follows:

 

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

SdFat sd;
SFEMP3Shield MP3player;

void setup() {

  Serial.begin(9600);

  //start the shield
  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();

  //start playing track 1
  MP3player.playTrack(1);
}

//do something else now
void loop() {

  Serial.println("I'm bored!");
  delay(2000);

}

That’s all it takes to get your Arduino to play an MP3 file using the shield.  How easy is that?

The library does have more functions and even error reporting. All of which can be found in the ReadMe file or in the Example Sketch included with the Library.

Source Code available on GitHub project pages: Sparkfun MP3 Player Shield Arduino Library

Or Direct Download here.

Using the SPI bus for something else as well?

I added some functions to make sure there will be no data collisions on the SPI bus caused by the MP3 decoder asking for more data at the wrong time. You need to wrap any SPI code you add to your project with these two functions. Here’s an example from my project that also has a shift register on the SPI bus:

  //disable interrupts to avoid collisions on the SPI bus between this code //and the MP3player library
  MP3player.pauseDataStream();

  //shift data
  tempIO = SPI.transfer(HIBYTE(output));
  tempIO<<8; 
  tempIO = SPI.transfer(LOBYTE(output));

  //latch output on shift registers
  digitalWrite(OUTLATCH,LOW);
  digitalWrite(OUTLATCH,HIGH);
  digitalWrite(OUTLATCH,LOW);

  //enable interrupts
  MP3player.resumeDataStream();

But you can’t stop the data stream to the MP3 Shield for too long before it runs out of data so be careful and try not to do too much stuff in between the functions.

 

Wanna be your own DJ?

New features! Requested in the comments below, there are now functions to skip around a playing track. You could fast forward, rewind, create loops, etc.

Here’s an example that will just open a file, jump to the 30 second mark (measured in milliseconds, 30 seconds = 30000 ms), and loop the song back every 750 ms:

MP3player.playTrack(1);

for(;;) {
MP3player.skipTo(30000);
delay(750);
}

If you want to know where you are in the current playback, do this:

time = MP3player.currentPosition();
Serial.println(time);

Both functions are not super accurate and depend on the bitrate of the file. The library should auto-detect the bitrate of the file but may fail on some MP3 files. If that happens, you have to set the bitrate manually like this:

MP3player.playTrack(1);
MP3player.setBitRate(192);

I can already see the cool DIY DJ equipment that could be made with this. Let me know if you have problems, and comment below if you ever make something cool with the library.

Troubleshooting

Pulled from the GitHub Project Page

The below is a list of basic questions to ask when attempting to determine the problem.

  • Did it initially PRINT the available RAM and Full Help Menu?
    • The MP3Shield_Library_Demo.ino example should initially provide a opening print indicating the amount of available SRAM and full menu help. If you don’t see this the problem is between your Target and IDE. And likely not this library
    • Is Serial Monitor set to the correct tty or com port and 115200 baud rate? Did you change the baud rate?
    • Reset the Arduino after Serial Monitor is open or send any key. It may have printed these prior to the Serial Monitor being started.
  • WHAT is the Error reported?
    • Is the Error Code is indicating a file problem.
    • Are the filenames 8.3 format? See below warning.
    • See also Error Codes
  • Did the SdCard LOAD?
    • Try reseating your SdCard.
  • Is it FAT(FAT16 or FAT32)?
    • If the Error Code is indicating problems with the INIT, VOLUME or Track not being successful. It is recommend to use SdFat Example Library’s QuickStart.ino as to see if it can access the card. Additionaly, SdInfo.ino may indicate if it can mount the card. Which may then need to formatted in FAT16 or FAT32. Where SdFormatter.ino can do this for you.
  • Are the needed files on the root?
    • Remember to put patch and audio track files on the SdCard after formatting.
    • Are the filenames 8.3 format? See below warning.
  • "Error code: 1 when \b trying to play track"
    • See the above Limitations. about Non-Blocking.
    • Remember to check your audio cables and volume.
  • Why do I only hear1 second of music, or less?
    • This symptom is typical of the interrupt not triggering the SFEMP3Shield::refill(). I bet repeatidly sendnig a track number will advance the play about one second at a time, then stop.
    • What board is it? Check Hardware Limitations. about Interrupts.
    • Are you trying the SFE provided test files ? Or some homemade mp3 files? The SFE test files are nice as they are Immediately LOUD.
    • Interrupt problems may cause mp3 files that have a quiet lead in (or ramp up of volume) to be falsely diagnosed as not playing at all. Where the first 1 second may not be loud enough to be heard.
  • Free RAM = 1090 Should be a base line of 1094
    • As a courtesy and good practice the provided example MP3Shield_Library_Demo.ino prints out the available remaining RAM, not statically allocated. And the actual available amount may depend on specific processor, IDE version, libraries and or other factors. A Uno built with IDE version 1.0.2 should have approximately 1094 bytes available from the example as is. And a Mega using a 2560 may show 6713, as it has more RAM.

Note

This library makes extensive use of SdFat Library as to retrieve the stream of audio data from the SdCard. Notably this is where most failures occur. Where some SdCard types and manufacturers are not supported by SdFat. Though SdFat Lib is at this time, supporting most known cards.

 

Warning

SdFatLib only supports 8.3 filenames. Long file names will not work. Use the 'd' menu command to display directory contents of the SdCard. "longfilename.mp3" will be converted to "longfi~1.mp3" . Where one can not predict the value of the 1. The DOS command of "dir \c /x" will list a cross reference, so that you know exactly, what is what.

Still Need Help?

Use the support forum to look for a solution or ask for help. Please don’t use the comments below to ask for help.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Share

Warning: count(): Parameter must be an array or an object that implements Countable in /homepages/46/d285670699/htdocs/bill/wp-includes/comment.php on line 879
  1. 590 Comments.

    • Hi ! Thank you for your answer. I discovered that the error was due to a damaged leg of the sparkfun shield. I fixed it. Now, I got a :

      Error code: 4 when trying to start MP3 player

      But I am using the demo files, so it desn’t sound like corrupted files.

      When I launch the sparkfun MP3 player example, i can hear only half a second of each mp3 files.

      Any idea ? Thanks again for your time !!

      • BillNo Gravatar says:

        Not really, my library is saying the decoder is not entering the correct mode. What does the Sparfun example say the SCI_Mode is when it starts up? it spits out that info over serial.

        • Haha…. problem solved…. so humiliating…..

          I began naming my files from 0 (track000.mp3). That was enough to mess all the thing up. Sorry for bothering and thanks for your library. It works great now !!

    • TristanNo Gravatar says:

      I have used your shield in conjunction with the keypad library.

      http://pastebin.com/JyDfwwCZ

      Now when I press a button, it plays the correct track, however after it has finished and I press a button, it stops.

      .playTrack(1) never completes and never returns anything.

      So it plays once then stops. I have used many combinations, but nothing seems to get the second track working.

    • TristanNo Gravatar says:

      I got the example code to work properly.
      I ran another test with the track repeating and that seemed to work fine.

      Currently it looks like keypad.getKey() and keypad.waitForKey() are the problem. I am in the process of finding the exact problem now.

      Thanks for your help, I didn’t notice the example was much more in-depth than I thought.

    • AllenNo Gravatar says:

      Actually, the MP3 shield is the only one device requires SPI. The other slave module I was working on does not require SPI, but it does need pin 12 and pin 13 to send data back and forth. I tried to put the code in between MP3player.pauseDataStream(); and MP3player.resumeDataStream();, but the code does not seen working properly.(ex. Serial.println(“print something”); isn’t able to print “print something” on my Serial monitor).

      I thought a way to do it. If there is anyway I can set Pin 10 (disable SS) constantly high, the MP3 shield should ignore any data from Pin 11, 12 and 13 send from Arduino. Do you know anyway to do this?

      -Allen

      • BillNo Gravatar says:

        The shield is not using pin 10 as the CS pin. It uses pins 6,7 and 9 for the select pins. Those already wait in the disable position until there is something to do.

        The problem is the library is written to use an interrupt. When the MP3 chip wants more data, it flips a pin which fires an interrupt asking for more data. Whatever your code is doing at the time is put on hold while the Arduino feed more data to the chip. This could cause problems, which is why there are “MP3player.pauseDataStream();” functions that the user can run to make the chip wait longer for data until something the end user is doing gets finished.

        But you are trying to go one step deeper and use the SPI bus pins for something else. Now you have to pause the data stream and disable the SPI bus before you can use the pins for something else. So your code is going to have to look like this:

        MP3player.pauseDataStream();
        SPI.end();

        //your code that uses pin 12 and 13
        //don’t forget you will need to set the pinModes() for those pins every time here before writing to them.

        SPI.begin();
        MP3player.resumeDataStream();

    • wayne hallNo Gravatar says:

      Hey Bill
      what amp and speakers would you suggest to go with the MP3 shield to make the music really sound great.

      loved your tutorial and intend to try the lib
      wayne

    • Josh BarthNo Gravatar says:

      I’ve been playing around with my project a bit today.

      I’d like to add a method to the library for parsing the SD card and grabbing the tag info for all the songs on the card, well at least for a single folder.

      This would allow for assimilating a more ideal description of the content into a list. I was thinking it would be best to do this for one track at a time and ship it off immediately, and run it in a loop up to the count of files in order to save RAM.

      2 issues present themselves as I ponder this.

      #1 – How do I get a count of the files on the folder in order to limit my loop count, or what other method might one use? Maybe catch an error on invalid filename?

      #2 – What would be the best way to add this? Modify/add a similar method like getTrackinfo? Would I be able to access “track” outside of the shield object after I instantiate it?

      • Josh BarthNo Gravatar says:

        Here is what I have so far — any suggestions or improvements?

        I’ve added the following to SFEMP3Shield.h in the appropriate places:

        static SdFile file;

        public:
        void listRoot();
        void getFileInfo(SdFile);

        private:
        char infobuffer[30];

        And I have added this to SFEMp3Shield.cpp:

        void SFEMP3Shield::listRoot(){
        char name[13];

        while (file.openNext(&root, O_READ)) {
        file.getFilename(name);
        Serial.print(name);
        Serial.print(“,”);

        getFileInfo(file);

        file.close();
        }

        }

        //reads and returns the file tag information
        void SFEMP3Shield::getFileInfo(SdFile file){

        //disable interupts
        pauseDataStream();

        //skip to end
        file.seekEnd((-128 + TRACK_TITLE));

        //read 30 bytes of tag informat at -128 + offset
        file.read(&infobuffer, 30);
        Serial.write((byte*)&infobuffer,30);
        Serial.print(“,”);

        //skip to end
        file.seekEnd((-128 + TRACK_ALBUM));

        //read 30 bytes of tag informat at -128 + offset
        file.read(&infobuffer, 30);
        Serial.write((byte*)&infobuffer,30);
        Serial.print(“,”);

        //skip to end
        file.seekEnd((-128 + TRACK_ARTIST));

        //read 30 bytes of tag informat at -128 + offset
        file.read(&infobuffer, 30);
        Serial.write((byte*)&infobuffer,30);
        Serial.println(“”);

        //renable interupt
        resumeDataStream();

        }

    • allenNo Gravatar says:

      If the music is playing, the SetVolume method will stop the song. Is there anyway that I can change the volume while the music is playing? thank you very much!

      • toddNo Gravatar says:

        I have the same question Allen.

      • BillNo Gravatar says:

        Hmm, never tested that. I don’t know if that’s a function of the chip (probably not) or a random result of a logic error I just realized existed in the library (The setvolume method waits for the DREQ pin to go high, which would cause the interrupt to fire and return when DREQ was no longer high).

        Try putting a pauseDataStream() before the setvolume call, and a resumeDataStream() after it and report back what happens.

        • toddNo Gravatar says:

          Thanks Bill. That seems to work for me.

          MP3player.pauseDataStream();
          MP3player.SetVolume(Left, Right);
          MP3player.resumeDataStream();

          Thanks for the awesome library.

          • BillNo Gravatar says:

            Ok, I fixed the library so you shouldn’t have to wrap the volume call in the datastream calls. It does that automatically now. If you test the new version, let me know if it works or not.

    • AllenNo Gravatar says:

      Hi, weird thing, I found SetVolume(20) has bigger volume than SetVolume(40). Does it supposes to be like this?

    • Patrick DirksNo Gravatar says:

      [ also posted on Sparkfun’s MP3 shield site ]

      Hi everyone – I’m trying to use Sparkfun’s MP3 shield through this fabulous SFEMP3Shield library together with Adafruit’s RGB LCD shield (see http://www.adafruit.com/products/714 and http://ladyada.net/make/rgblcdshield/ for any details I may leave out) to play tracks from the SD card and put their names up on the LCD. I have buttons to skip forward and repeat but it looks like trying to do I/O to the LCD shield (which uses I2C using the ‘Wire’ library on pins 4 & 5) while I’m playing a song somehow makes it such that at the end of the song, when playback stops, the SdFatlib code can’t find any data on the SD any longer. Trying to just play a random song from the SD works for one song, then no more. I can skip around between songs and start different songs fine but as soon as I let one play out, the sketch can’t find any other tracks, even those it could’ve played a second ago.

      If I disable the code to do control the LCD (which appears to work fine, BTW) during playback (initializing it and putting up an initial message seems to do no harm) MP3 playback works as expected, playing random songs all day. The moment I re-enbable LCD display it fails (with playback error 2, file not found) after the first track.

      Code size seems around the limit – about 20K or so, but seems OK… Any ideas what may be going on here? The MP3 shield is using SPI, the LCD using i2c on address 0x20 and they seem to be using different pins – where’s the conflict? Any hints greatly appreciated!

      Thanks in advance,
      -Patrick.

    • DanBNo Gravatar says:

      Curious: can you stack multiple shields on top and play ‘multichannel’ tracks simultaneously?

      Regards!

    • DanBNo Gravatar says:

      hmm, I did a test with 2 boards on top of each others.
      works fine, but there is one problem: when I use an external power source, I have to manually reset it, otherwise it won’t run the code…

      weird stuff right!?

      anybody got a clue what it could be?

      • BillNo Gravatar says:

        2 mp3 boards worked? Did you move the second boards’s CS pins?

      • DanBNo Gravatar says:

        wow, shit, it didn’t actually work.
        I was playing the same track on both sd cards.
        but when I changed it to two different tracks it actually turned out to be only reading one sd slot!

        funky error!

        you reckon removing the second board CS pins will solve this?

        cheers!

    • BabakNo Gravatar says:

      I was having freezing when Stopping and Playing several times, in the end I tracked it down to this line in the library:

      Mp3WriteRegister(SCI_MODE, 0x48, SM_RESET);

      that came with the ominous warning: “//easier then the way your SUPPOSE to do it by the manual, same result as much as I can tell.”

      I commented it out (scary!) and the freezing problem disappeared. I also found that I no longer needed to put in delays before playTrack calls.

      Thanks again for this library, it helped this beginner get on his feet and going!

    • ToddNo Gravatar says:

      For some reason with the latest version of the library I am getting stuck on the following line.

      MP3player.begin();

      My code can’t get past it. The Sparkfun example works fine, but yours does not. Any ideas or ways to go back to the previous version of the library?

    • ToddNo Gravatar says:

      If I put a print statement directly before and after Mp3player.begin();, I only see the statement before in the serial monitor.

    • Patrick DirksNo Gravatar says:

      Hi,

      I suspect there’s a race condition lurking somewhere between the ending of a song’s playback and the pauseDataStream / resumeDataStream functions. I believe the problems I posted about earlier with a suspected incompatibility with Adafruit’s RGB shield and its code is actually strictly related to my (unnecessarily, I now realize) calling pause / resume at times when a song could end.

      This problem is easily reproduced: take the MP3Shield Library demo and replace the main loop with one that just plays a given track over and over and call MP3player.pauseDataStream(); MP3player.resumeDataStream() in the main loop (no need for anything in between). After the usual initialization of serial I/O and the MP3 shield I used the following for my main loop; you’d obviously have to pick different filenames for testing:

      void loop() {
      static char testTrackName[] = “D17-T23.mp3”;

      if (!MP3player.isPlaying()) {
      Serial.print(“Starting “); Serial.println(testTrackName);
      byte result = MP3player.playMP3(testTrackName);

      //check result, see readme for error codes.
      if(result != 0) {
      Serial.print(“Error code: “);
      Serial.print(result);
      Serial.print(” when trying to play ‘”);
      Serial.print(testTrackName);
      Serial.println(“‘?”);

      delay(3000);
      }
      }

      MP3player.pauseDataStream();
      MP3player.resumeDataStream();
      }

      The resulting serial output usually stops altogether pretty quickly, usually after playing the first song. There’s no error returned, output just halts. The typical output is just

      Initializing the MP3 Player Shield…
      Starting D17-T23.mp3
      Starting D17-T23.mp3

      Commenting out the MP3player.pauseDataStream() / MP3player.resumeDataStream() pair makes the whole loop work flawlessly.

      Any idea what may be going on?

      • PatrickDNo Gravatar says:

        Can anyone reproduce this hang using their own MP3 track?

        This calling pauseDataStream() / resumeDataStream repeatedly consistently stops all playback for me and I wonder if there’s something about calling either of them when playback’s already reached the end of a song that messes up the shield?

        • BillNo Gravatar says:

          IT shouldn’t, there’s an internal flag ‘playing’ that the library check to confirm whether it should or shouldn’t do something. Remember, calling pauseDataStream() stops feeding the mp3 decoder ship. If you are not starting it back fast enough (withing 80ms or so) or stopping it to often, then the chip chokes and I don’t know what behavior it will cause. I’ll test that shortly. In my first application, I was also using the SPI bus to read 16bit IO shift registers at 20 Hz. I didn’t experience any problems with that. What are you trying to do?

          • PatrickDNo Gravatar says:

            Hi Bill – I know it *shouldn’t* but it seems to hang quite reliably. Try the program above (or anything that loops, starting some track over if isPlaying() returns false, and calling pauseDataStream() / resumeDataStream( ) every time through the loop) and see if you make it to even half a dozen iterations.

            This can hardly be a starvation issue, BTW – it hung even with NOTHING done between pauseDataStream and resumeDataStream.

            This is actually not urgent to me personally – I thought I ran into some incompatibility with the shield but it turns out I didn’t need to be calling these two routines at all – but it took me quite a while to figure out why my initial sketch was hanging and someone else may still run into it for real.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.