Design Point of View- My design point of view was the make a MP3 player that would sit on a desk and be able to combine a model of the enterprise, a desk lamp, and a speaker. I wanted to make something that both looks cool and is very functional. I wanted it to alos be mobile so it would not rely on a wall outlet to play music or light a place up with it s big LEDs.
#include <SD.h>
#include <EEPROM.h>
#include <mp3.h>
#include <mp3conf.h>
// include the adafruit pcd8544 & gfx libraries for a nokia 5110 graphic lcd.
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.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
// now assign pins for the graphic lcd (carried over from the etch-a-sketch).
#define lcd_clk 7 // 'serial clock' connect to lcd's clk pin
#define lcd_din 6 // 'serial data input' connects to din pin
#define lcd_dc 5 // 'data/command input' connect to d/c pin
#define lcd_cs -1 // 'slave chip select' connects to cs pin
#define lcd_rst 4 // 'reset' connects to graphic lcd rst 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 175 // 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.
Adafruit_PCD8544 lcd = Adafruit_PCD8544(lcd_clk, lcd_din, lcd_dc, lcd_cs, lcd_rst);
// '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.
get_current_song_as_fn();
// 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.
print_title_to_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.
buttonState = digitalRead(buttonPin);
if(buttonState == HIGH){
state = 1;
}
if( state == 1){
while(true){
delay(100);
buttonState = digitalRead(buttonPin);
if(buttonState == HIGH){
delay(100);
break;
if (bytes_to_read < read_buffer) {
sd_file.close();
// 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) {
mp3_play();
}
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)) {
current_song++;
sd_file_open();
}
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() {
// if using a graphic lcd, initialize with contrast, then setup the screen.
lcd.begin(50);
lcd.print("Barebones Mp3!");
lcd.display();
// 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);
Mp3.volume(mp3_vol);
// initialize the microsd (which checks the card, volume and root objects).
sd_card_setup();
// putting all of the root directory's songs into eeprom saves flash space.
sd_dir_setup();
// 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.
sd_file_open();
}
// 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:
dir_play();
break;
case MP3_PLAY:
mp3_play();
break;
case PAUSED:
break;
}
}