• If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Stop wasting time looking for files and revisions. Connect your Gmail, DriveDropbox, and Slack accounts and in less than 2 minutes, Dokkio will automatically organize all your file attachments. Learn more and claim your free account.


Derrenbacher Michael Final

Page history last edited by Michael Derrenbacher 4 years, 11 months ago

     In order to get a good idea of what I wanted, I thought a lot during the project’s early stages. I wanted something I would use everyday, and something that would make life\easier. Most of the time when I play music, it’s when I’m on a run, and sometimes Internet connection can get spotty. A portable mp3 player that has songs directly download alleviates this problem. Another annoyance when running with a phone is the shape. The flat and thin design can make it hard to grip. Some people combat this by an armband to hold their phone. This frees their hands but also makes it so changing songs and pausing is more difficult. My solution to these plagues is to make an object that runners have used for many years, a baton.


     The premise behind this idea is that runners are used to holding batons and doing it more will get them accustomed to running with them so that during real races they will have experience. If someone is running in a group, and someone want to chose the song, the person holding the player can pass it to them like they would with a baton during a race.



     The execution was not a strong part of my project. I only had the project on a breadboard due to time constraints from SD card troubles and general lack of time management. In the end, it only could play songs in a loop without pausing, volume control, and song skipping. I plan on moving forward from this design by adding a pause button using the attachInterrupt command, adding volume buttons with a simple counter increase, and a song skip by moving the song counter over by one. I will then perf board it to minimize area taken up to make the baton as slim as possible.




     When it has a battery, It will work very well.  Right now I have to carry a laptop and breadboard to play music. It will do exactly what I want it to do after I finish, play music and be compact enough to run with.






As you can see, it's slightly clunky.








     I started off by hooking up the decoder to the 4050 and arduino microcontroller so that the speaker could play sounds. I then added SD card reading capabilities so that it could play music that I can (legally) download from the Internet. After that I plan to add pause functionality to enhance the user experience. 1 



     What I did was very simple. I connected the mp3 decoder to the arduino and 4050 level shifter. This was the main wiring part of my project. The other wiring part was setting up communications with the SD card so that the mp3 files could be read.


The code of the project:





// first step is to include (arduino) sd, eeprom and (our own) mp3 libraries.


#include <SD.h>

#include <SPI.h>

#include <EEPROM.h>


#include <mp3.h>

#include <mp3conf.h>



// setup microsd and decoder chip select pins, and the decoder-specific pins.


#define sd_cs         17        // 'chip select' line for the microsd card


#define mp3_cs        A0        // 'command chip select' connect to cs pin

#define mp3_dcs       A1        // 'data chip select' connect to bsync pin

#define mp3_rst       -1        // 'reset' connects to decoder's reset pin

#define mp3_dreq      A2        // 'data request line' connect to dreq pin



// 'read_buffer' is the amount of data read from microsd and sent to decoder.

// it's probably best to keep this a factor of 2, up to about 1kb (2kb is the

// max). you might change this if you experienced skips during song playback.


#define read_buffer  512        // size (bytes) of the microsd read buffer

#define mp3_vol      254        // default volume. range min=0 and max=254


// file names are 13 bytes max (8 + '.' + 3 + '\0'), and the file list should

// fit into the eeprom. for example, 13 * 40 = 520 bytes of eeprom are needed

// to store a list of 40 songs. if you use shorter file names, or if your mcu

// has more eeprom, you can change these.


#define max_name_len  13

#define max_num_songs 40


// id3v2 tags have variable-length song titles. that length is indicated in 4

// bytes within the tag. id3v1 tags also have variable-length song titles, up

// to 30 bytes maximum, but the length is not indicated within the tag. using

// 60 bytes here is a compromise between holding most titles and saving sram.


// if you increase this above 255, look for and change 'for' loop index types

// so as to not to overflow the unsigned char data type.


#define max_title_len 60


// ---- global variables ----------------------------------------------------


// instantiate a graphic lcd object using the pins that we #define'd earlier.

// comment out the graphics lines to save memory if you're not using the lcd.



// 'File' is a wrapper of the 'SdFile' data type from the sd utility library.


File sd_file;                   // object to represent a file on a microsd


// store the number of songs in this directory, and the current song to play.


unsigned char num_songs = 0, current_song = 0;


// an array to hold the current_song's file name in ram. every file's name is

// stored longer-term in the eeprom. this array is used in 'sd_file.open()'.


char fn[max_name_len];


// an array to hold the current_song's title in ram. it needs 1 extra char to

// hold the '\0' that indicates the end of a character string. the song title

// is found in 'get_title_from_id3tag()'.


char title[max_title_len + 1];


// the program runs as a state machine. the 'state' enum includes the states.

// 'current_state' is the default as the program starts. add new states here.


enum state { DIR_PLAY, MP3_PLAY, PAUSED };

state current_state = DIR_PLAY;


//---- module functions -----------------------------------------------------


// you must open a song file that you want to play using 'sd_file_open' prior

// to fetching song data from the file. you can only open one file at a time.


void sd_file_open() {  

  // first, find the file name (that's stored in eeprom) of the current song.




  // then open the file using the name we just found (stored in 'fn' global).


  sd_file = SD.open(fn, FILE_READ);


  // find the current song's title tag (if present) then print it to the lcd. 





// read a number of bytes from the microsd card, then forward them to the Mp3

// library's 'play' function, which streams them out to the decoder chip.


void mp3_play() {

  unsigned char bytes[read_buffer]; // buffer to read and send to the decoder

  unsigned int bytes_to_read;       // number of bytes read from microsd card


  // first fill the 'bytes' buffer with (up to) 'read_buffer' count of bytes.

  // that happens through the 'sd_file.read()' call, which returns the actual

  // number of bytes that were read (which can be fewer than 'read_buffer' if

  // at the end of the file). then send the retrieved bytes out to be played.


  // 'sd_file.read()' manages the index pointer into the file and knows where

  // to start reading the next batch of bytes. 'Mp3.play()' manages the index

  // pointer into the 'bytes' buffer and knows how to send it to the decoder.


  bytes_to_read = sd_file.read(bytes, read_buffer);

  Mp3.play(bytes, bytes_to_read);


  // 'bytes_to_read' is only smaller than 'read_buffer' when the song's over.


  if (bytes_to_read < read_buffer) {



    // if we've been in the MP3_PLAY state, then we want to pause the player.


    if (current_state == MP3_PLAY) {

      current_state == PAUSED;





// continue to play the current (playing) song, until there are no more songs

// in the directory to play. 2 other sd library methods (that we haven't used

// here) can help track your progress while playing songs: 'sd_file.size()' &

// 'sd_file.position()'. you can use these to show say, the percent of a song

// that has already played.


void dir_play() {

  if (sd_file) {



  else {

    // since 'sd_file' isn't open, the recently playing song must have ended.

    // increment the index, and open the next song, unless it's the last song

    // in the directory. in that case, just set the state to PAUSED.


    if (current_song < (num_songs - 1)) {





    else {

      current_state = PAUSED;





// ---- setup and loop ------------------------------------------------------


// setup is pretty straightforward. initialize serial communication (used for

// the following error messages), mp3 library, microsd card objects, then the

// graphic lcd. then open the first song in the root library to play.


void setup() {



  // initialize the mp3 library, and set default volume. 'mp3_cs' is the chip

  // select, 'dcs' is data chip select, 'rst' is reset and 'dreq' is the data

  // request. the decoder sets the 'dreq' line (automatically) to signal that

  // its input buffer can accommodate 32 more bytes of incoming song data.


  // the decoder's default state prevents the spi bus from working with other

  // spi devices, so we initialize it first.


  Mp3.begin(mp3_cs, mp3_dcs, mp3_rst, mp3_dreq);



  // initialize the microsd (which checks the card, volume and root objects).




  // putting all of the root directory's songs into eeprom saves flash space.




  // the program is setup to enter DIR_PLAY mode immediately, so this call to

  // open the root directory before reaching the state machine is needed.






// the state machine is setup (at least, at first) to open the microsd card's

// root directory, play all of the songs within it, close the root directory,

// and then stop playing. change these, or add new actions here.


// the DIR_PLAY state plays all of the songs in a directory and then switches

// into PAUSED when done. the MP3_PLAY state plays one specific song and then

// switches into PAUSED. this sample player doesn't enter the MP3_PLAY state,

// as its goal (for now) is just to play all the songs. you can change that.


void loop() {

  switch(current_state) {


    case DIR_PLAY:




    case MP3_PLAY:




    case PAUSED:








Comments (1)

David S said

at 2:16 pm on Aug 18, 2015

Your design point of view is clear, makes sense, and describes what could be a real-world product. It does seem to address what could be a real need for many runners. We especially like the idea of being able to “hand off” music to another runner, just like in a relay race. Nice!

Your device sketch does a good job of representing this idea. Consider, however, that in the same way that adjusting controls on your arm isn’t convenient for current players, looking at a display while running also isn’t convenient. Perhaps the project doesn’t need a display—other than maybe some colored LEDs to indicate state—at all?

The Verplank diagram is fine, and, we wish that you had made it a bit more visual, as images are both more informative for the viewer, and require the designer (you) to more carefully work out the details required to evaluate, build, and eventually use, the device. Especially for the display, NA doesn’t quite seem right, as ALL devices have displays of some sort, even if it’s just a clean, unadorned housing.

The videos are cute, although not that representative, other than conceptually, and the first one is scary: thanks for posting the update showing that the player is still working.

It’s good to read your plans for how to move forward with the project. We wish that you were able to get past your microSD troubles and time constraints. It will be a challenge to build a perfboard that houses your project in the small footprint that you have in mind, and, we’re confident that you can do so, once you have time to work on things again. Please send us an update when you do complete the project, and keep running and listening!

David, Dongao, Praveen, Samyuktha, Tian, Xiangyu, Zahra

You don't have permission to comment on this page.