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

  • You already know Dokkio is an AI-powered assistant to organize & manage your digital files & messages. Very soon, Dokkio will support Outlook as well as One Drive. Check it out today!

View
 

final project ee47

Page history last edited by neto 11 years, 8 months ago

Design point of view (what are you designing a player for?)

This device is designed for people that don’t want to waste time in learning how to use a device (or don’t have the ability to learn, like somebody drunk), to play the music they want.

 

Verplank diagram

Photos of paper prototype

 

State diagram

Project code

/*

* Edited versión by Javier Ernesto Flores Robles, jeflro@gmail.com

* Last edit August 18, 2012

* originally based on 2011 david sirkin sirkin@cdr.stanford.edu work

* based on frank zhao's player: http://frank.circleofcurrent.com/

* utilities adapted from previous versions of the functions by matthew seal.

*

*/

 

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

 

#include <SD.h>

#include <EEPROM.h>

 

#include <mp3.h>

#include <mp3conf.h>

 

//#define ENCODER_OPTIMIZE_INTERRUPTS // not working right

#include <Encoder.h>

 

#include <nokia_5110_lcd.h>

/*IF we are not using the Graphics or Bitmap capabilities

of the LCD, save code space with these #defines*/

#define NO_GRAPHICS

#define NO_BITMAP

 

// setup microsd, decoder, and lcd chip pins

 

#define sd_cs      12      // 'chip select' for microsd card

#define mp3_cs     21      // 'command chip select' to cs pin

 

#define dcs        20      // 'data chip select' to bsync pin

#define rst        18      // 'reset' to decoder's reset pin

#define dreq       19    

// 'data request line' to dreq pin

 

//LCD PINS

#define LCD_PWR   10

#define LCD_SCE   16

#define LCD_RESET 17

#define LCD_DC 4

#define LCD_BL 13

 

//Interrupts PINS

#define redButton   8

#define encButton   7

 

// Color PINS

#define redPin   9

#define bluePin   14

#define greenPin   15

 

//eeprom variables:

#define eeVol 601

#define eeLights 600

#define eeContrast 602

 

// read_buffer is the amount of data read from microsd, then sent to decoder.

 

#define read_buffer   512     // size of the microsd read buffer

int mp3_vol;            // default volume: 0=min, 254=max

 

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

 

// next steps, declare the variables used later to represent microsd objects.

 

Sd2Card  card;                // top-level represenation of card

SdVolume volume;              // sd partition, not audio volume

SdFile   sd_root, sd_file;    // sd_file is the child of sd_root

 

// declare lcd object as well

Nokia_5110_lcd lcd(LCD_PWR, LCD_DC, LCD_SCE, LCD_RESET);

 

// declare the encoder

Encoder myEnc(6, 5);

 

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

 

unsigned char num_songs = 0, current_song = 0, mp3_selected=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 for sd_file.open().

 

char fn[max_name_len];

 

// 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, IDLE, PAUSE, MENU, SETTINGS,CHOOSE };

volatile state current_state = MENU;

volatile state paused_state = MENU;

 

//menu states

int selected = 0;

enum menu_state {MENU_PLAY_ALL,MENU_CHOOSING, MENU_SETTINGS };

 

// settings states

int sett_selected = 0;

enum sett_state {SETT_BGLON, SETT_BGLOFF};

 

 

void rgbColor (int Re, int Gr, int Bl) {

  if (EEPROM.read(eeLights)>0) {

analogWrite( redPin ,255-Re);

analogWrite(bluePin  , 255-Bl);

analogWrite(greenPin, 255-Gr);

 }  else {

analogWrite( redPin ,255);

analogWrite(bluePin  , 255);

analogWrite(greenPin, 255);

 }

}

// you must open any 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() {

 map_current_song_to_fn();

 sd_file.open(&sd_root, fn, FILE_READ);

 

 // if you prefer to work with the current song index (only) instead of file

 // names, this version of the open command should also work for you:

 

 // sd_file.open(&sd_root, current_song, FILE_READ);

}

 

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

 

 // send read_buffer bytes to be played. Mp3.play() tracks the index pointer

 // within the song being played of where to get the next read_buffer bytes.

 

 bytes_to_read = sd_file.read(bytes, read_buffer);

 Mp3.play(bytes, bytes_to_read);

 

 // bytes_to_read should only be less than read_buffer when the song's over.

 

 if(bytes_to_read < read_buffer) {

sd_file.close();

current_state = IDLE;

 }

}

 

 

long oldPosition  = -999;

void checkVol () {

 

 long newPosition = myEnc.read();

 if (newPosition != oldPosition) {

rgbColor(255,30,30);

 

mp3_vol += newPosition - oldPosition;

 

  if (mp3_vol < 1 )

    mp3_vol = 1;

  else if (mp3_vol > 255)

    mp3_vol = 255;

 

  EEPROM.write(eeVol,mp3_vol);

   Mp3.volume(mp3_vol);

  oldPosition = newPosition;

 

 }

 

}

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

// in the directory to play.

void dir_play() {

 lcd.writeString( 0, 0, "Playing:   ", MODE_NORMAL);

 lcd.writeString( 0, 1, fn, MODE_NORMAL);

 checkVol();

 

 if (current_song < num_songs) {

mp3_play();

 

// if current_state is IDLE, then the currently playing song just ended.

// in that case, increment to get the next song to play, open that file,

// and return to the DIR_PLAY state (which will then play that song).

    // if we played the last part of the last song, we don't do anything,

    // and the current_state is already set to IDLE from mp3_play()

 

if (current_state == IDLE && current_song < (num_songs - 1)) {

   current_song++;

   sd_file_open();

   current_state = DIR_PLAY;

 

}

  }

}

 

//The functions for the buttons

void rightButtonPressed (){

 

 oldPosition = myEnc.read();

//  Serial.println("right int");

 static unsigned long last_interrupt_time = 0;

 unsigned long interrupt_time = millis();

 // If interrupts come faster than 200ms, assume it's a bounce and ignore

 if (interrupt_time - last_interrupt_time > 200)

 {

// Serial.println("button");

switch(current_state) {

 

case MENU:

// Serial.println("menu push");

  lcd.clear();

 

  if (selected == MENU_PLAY_ALL){

    current_state = DIR_PLAY;

    oldPosition  = myEnc.read();

  }

  else if (selected ==  MENU_SETTINGS){

    myEnc.write(sett_selected*4);

    current_state = SETTINGS;

 

  }else if (selected ==  MENU_CHOOSING){

    myEnc.write(mp3_selected*4);

    current_state = CHOOSE;

  }

  break;

 

case DIR_PLAY:

  paused_state = current_state;

  current_state = PAUSE;

  break;

 

case MP3_PLAY:

  paused_state = current_state;

  current_state = PAUSE;

  break;

 

case IDLE:

  break;

 

case PAUSE:

  current_state = paused_state;

  break;

 

case CHOOSE:

 

  sd_file.close();

  Mp3.clear_buffer();

  current_song = mp3_selected;

  sd_file_open();

  current_state = DIR_PLAY;

  break;

 

case SETTINGS:

  if (sett_selected == SETT_BGLON){

    digitalWrite(LCD_BL, HIGH);

    EEPROM.write(eeLights,1);

    rgbColor(150,20,255);

  }

  else if (sett_selected ==  SETT_BGLOFF){

 

    digitalWrite(LCD_BL, LOW);

    EEPROM.write(eeLights,0);

    rgbColor(0,0,0);

  }

  break;

 

}

 }

 last_interrupt_time = interrupt_time;

 

 

}

 

void leftButtonPressed (){

 

 static unsigned long last_interrupt_time = 0;

 unsigned long interrupt_time = millis();

 // If interrupts come faster than 200ms, assume it's a bounce and ignore

 if (interrupt_time - last_interrupt_time > 200)

 {

switch(current_state) {

 

case MENU:

 

  break;

 

case DIR_PLAY:

  current_state = MENU;

  break;

 

case MP3_PLAY:

  current_state = MENU;

  break;

 

case IDLE:

  break;

 

case PAUSE:

 

  break;

 

case SETTINGS:

current_state = MENU;

  break;

 

case CHOOSE:

current_state = MENU;

  break;

 

}

 }

 last_interrupt_time = interrupt_time;

}

 

void settings() {

 if (myEnc.read()%4 == 0){

if (myEnc.read()/4 > sett_selected)

  sett_selected++;

else if (myEnc.read()/4 < sett_selected)

  sett_selected--;

 

if (sett_selected < SETT_BGLON )

  sett_selected = SETT_BGLON;

else if (sett_selected > SETT_BGLOFF)

  sett_selected = SETT_BGLOFF;

 

myEnc.write (sett_selected*4);

 

 

 }

  if (sett_selected == SETT_BGLON)

    lcd.writeString( 0, SETT_BGLON, "Lights ON ", MODE_INVERSE);

else

    lcd.writeString( 0, SETT_BGLON, "Lights ON ", MODE_NORMAL);

 

if (sett_selected == SETT_BGLOFF)

    lcd.writeString( 0, SETT_BGLOFF, "Lights OFF ", MODE_INVERSE);

else

    lcd.writeString( 0, SETT_BGLOFF, "Lights OFF ", MODE_NORMAL);

 

 

}

 

void menu (){

// lcd.clear();

 if (myEnc.read()%4 == 0){

if (myEnc.read()/4 > selected)

  selected++;

else if (myEnc.read()/4 < selected)

  selected--;

 

if (selected < MENU_PLAY_ALL )

  selected = MENU_PLAY_ALL;

else if (selected > MENU_SETTINGS)

  selected = MENU_SETTINGS;

 

myEnc.write (selected*4);

 

 

 }

 if (selected == MENU_PLAY_ALL)

    lcd.writeString( 0, MENU_PLAY_ALL, "Play all   ", MODE_INVERSE);

else

    lcd.writeString( 0, MENU_PLAY_ALL, "Play all   ", MODE_NORMAL);

 

if (selected == MENU_SETTINGS)

    lcd.writeString( 0, MENU_SETTINGS, "Settings   ", MODE_INVERSE);

else

    lcd.writeString( 0, MENU_SETTINGS, "Settings   ", MODE_NORMAL);

 

if (selected == MENU_CHOOSING)

    lcd.writeString( 0, MENU_CHOOSING, "Choose a song ", MODE_INVERSE);

else

    lcd.writeString( 0, MENU_CHOOSING, "Choose a song ", MODE_NORMAL);

}

 

int pastState = current_state;

 

void celarScrn() {

 

 if (pastState != current_state)

 {

pastState = current_state;

lcd.clear();

 }

}

 

void choose(){

 

 celarScrn();

 if (myEnc.read()%4 == 0){

if (myEnc.read()/4 > mp3_selected)

  mp3_selected++;

else if (myEnc.read()/4 < mp3_selected)

  mp3_selected--;

 

if (mp3_selected > num_songs*5 )

  mp3_selected = 0;

else if (mp3_selected > num_songs-1)

  mp3_selected = num_songs-1;

 

myEnc.write (mp3_selected*4);

 

 

 }

 

 for(int i=0; i <num_songs; i ++) {

lcd.gotoXY(0,i);

for (int j = 0; j < max_name_len; j++){

  if (EEPROM.read(i * max_name_len + j) == '\0') {

    break;

  }

  if (mp3_selected == i)

    lcd.writeChar(EEPROM.read(i * max_name_len + j), MODE_INVERSE);

  else

    lcd.writeChar(EEPROM.read(i * max_name_len + j), MODE_NORMAL);

}

 }

 

 

 

}

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

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

// the first song in the root library to play.

 

void setup() {

 

 Serial.begin(9600);

 Serial.println("serial on");

 

 pinMode(SS_PIN, OUTPUT);  //SS_PIN must be output to use SPI

 pinMode(LCD_BL, OUTPUT);

 pinMode(redPin, OUTPUT);

 pinMode(bluePin, OUTPUT);

 pinMode(greenPin, OUTPUT);

 

 // the default state of the mp3 decoder chip keeps the SPI bus from

 // working with other SPI devices, so we have to initialize it first.

 Mp3.begin(mp3_cs, dcs, rst, dreq);

 

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

 sd_card_setup();

 

 //initialize the LCD

 int contrast = EEPROM.read(eeContrast);

 if (contrast == 0)

contrast = 50;  

 lcd.init(contrast); //parameter is contrast value, between 0 [low] and 127 [high]

 lcd.clear();

 

 // 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 raises the dreq line (automatically) to signal that

 // it's input buffer can accommodate 32 more bytes of incoming song data.

 // we need to set the SPI speed with the mp3 initialize function since

 // it is the limiting factor, so we call its init function again.

 

 Mp3.begin(mp3_cs, dcs, rst, dreq);

 

 

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

 

 //Test that LCD still works with other SPI devices

 

 pinMode(redButton, INPUT_PULLUP);

 pinMode(encButton, INPUT);

 attachInterrupt(2, rightButtonPressed, FALLING  );

 attachInterrupt(3, leftButtonPressed, RISING  );

 

 

 if (EEPROM.read(eeLights)>0) {

digitalWrite(LCD_BL, HIGH);

 } else

rgbColor (0,0,0);

 

 

 mp3_vol = EEPROM.read(eeVol);

 if (mp3_vol == 0)

mp3_vol= 175;  

 

 Mp3.volume(mp3_vol);

}

 

 

// 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 IDLE when done. the MP3_PLAY state plays one specified song, and then

// switches into IDLE. this example program 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 MENU:

  celarScrn();

        rgbColor(20,250,250);

  lcd.writeString( 0, 5, "menu   ", MODE_NORMAL);

//   lcd.update();

  menu();

  break;

 

case DIR_PLAY:

  celarScrn();

    rgbColor(20,255,30);

  lcd.writeString( 0, 5, "dir play  ", MODE_NORMAL);

//   lcd.update();

  dir_play();

  break;

 

case MP3_PLAY:

  lcd.writeString( 0, 5, "mp3 play  ", MODE_NORMAL);

//   lcd.update();

  mp3_play();

  break;

 

case IDLE:

  lcd.writeString( 0, 5, "idle ", MODE_NORMAL);

//   lcd.update();

  break;

 

case PAUSE:

  lcd.writeString( 0, 5, "pause   ", MODE_NORMAL);

//   lcd.update();

  break;

 

case SETTINGS:

   rgbColor(255,255,255);

  lcd.writeString( 0, 5, "settings   ", MODE_NORMAL);

//   lcd.update();

  settings();

  break;

 

case CHOOSE:

   rgbColor(0,0,255);

  lcd.writeString( 0, 5, "choosing   ", MODE_NORMAL);

//   lcd.update();

  choose();

  break;

 }

}

 

Video of the final working player in use

 

 

short video of the making process

Comments (1)

yusi chen said

at 2:44 pm on Aug 22, 2012

Awesome project!

We like that you include scenarios, figures of brainstorm process, prototype design and state diagram to clarify your design process. Your idea using only 2 buttons to control your mp3 is wonderful. The multiuse of the rotation button is awesome!
We know that time is limited for summer quarter final project. We do hope you have more time to add subtitles or narratives to your making process video, which will be more illustrative.
If you can redo your project, we wish you can add more features and details to your product. For example, maybe integrated some sensors, like accelerometer and IR sensors, so that your product will be more interactive! Also, maybe you can vertically integrate your devices, so that the total area of your device will be even smaller!

Wonderful job!

Best,

David, Yusi, Kai and Ben

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