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

EE 47 Final Project

Page history last edited by Jomar 12 years, 10 months ago

Jomar Sevilla

EE47 Final Project Documentation

Design Vision/Point of View:

My design is inspired by those antique music boxes that play old music by opening or closing the top’s lid. Therefore, I wanted to make a device that needs no other user input then the opening of a box. For the box, I also made it a priority to make all the circuits and components very visible. I did this so that I can fully explain to casual onlookers how everything works and the role of each component in the project. This turned out to be very cool later when friends and family would me about different aspects of the project and I could clearly and simply demystify the project.

In my vision, the SD card can easily be removed and replaced to play music from other cards. On my card, I saved Johnny Nash’s “I Can See Clearly Now”. The optimistic upbeat song is a nice surprise for anyone who opens the box. The old song from 1972 matches the project’s theme of an mp3 in an old-fashioned cigar box. And lastly the song fits well with the theme of being able to ‘see’ or visually understand all the distinguishable components and electrical concepts of the mp3 player.

Music Box Verplank Diagrams:

Paper Prototypes and State Diagram:

(Sorry I used the back of the page for another design and the green marker bled through)

Components Necessary:

  • Arduino Nano

  • VS1053 MP3/MIDI decoder

  • Graphical LCD screen (for debugging and later for user interface)

  • Fancy Cigar Box (size depends on your design; my box had width 5.5in ,length 7.5 in, height 2in.)

  • Two 8-Ohm speakers

  • 9 volt battery or 5 volt battery

  • 5 volt regulator

  • 3.3 volt regulator

  • One Snap-Action Switch

  • Breadboard or Stripboard

Process:

Because the weekend leading up to the final presentation was also Finals week, my final execution process was hectic, but still manageable. Because I was under a pressured time crunch, I had to scrap some earlier ambitions and complicated code that I had written in order to have something presentable. Because I was short on money, time, and transportation to RadioShack, I needed to make sure all my parts were available from EE47’s lab. Fortunately all the materials listed above were in the lab or readily available.

In the end, my project was successful in fulfilling the basic purposes I was setup for. During the Final Presentations on Wednesday the music box was half working because I still required the USB power in order to play music. However after some helpful advice from Wendy and past students (Cameron S. and Tom M.), I was able to make the music box battery freestanding and ready to be brought anywhere. This is shown in the video. I can now bring my functioning project anywhere, but still be able to make future extensions over the summer and when I have more experience with circuitry and Arduino coding.

Project Code:

SongPlayer.pde

/*

* 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)

* and then reformatted by Jomar Sevilla for his Music Box Final Project

*/

// 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 250 // output volume range is 0 to 254

 

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

}

 

 

#define NO_GRAPHICS

#define NO_BITMAP

#include <nokia_5110_lcd.h>

 

//LCD PINS

#define LCD_PWR 6

#define LCD_SCE 9

#define LCD_RESET 8

#define LCD_DC 7

 

//SPI PINS

#define PIN_SDIN 11

#define PIN_SCLK 13

/**

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

 

void spi_init(){

pinMode(10, INPUT); // SS pin, should be pulled high.

pinMode(12, INPUT); // MISO

pinMode(11, OUTPUT); //MOSI

pinMode(13, OUTPUT); //SCLK

digitalWrite(10, HIGH);

SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0);

}

**/

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

//lcd.clear();

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

#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() {

/**

CLKPR = 0x80;

CLKPR = 0x1;

spi_init();

lcd.init();

lcd.clear();

**/

Serial.begin(9600); // initialize the serial terminal

pinMode(10, OUTPUT); // change this to 53 on a mega

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

 

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

}

 

// Do nothing for now

void loop() {

while (1);

}

Video of Final Project:

http://www.youtube.com/watch?v=LS3eijq-Ijw

Music Box Movie Description: This is the final presentation for my EE47 class taken at the hotel. Sorry about bad video quality, taken from my phone.

Future Installments:

  • Next Song and Previous Song Buttons

  • Volume Adjustor (using potetiometer)

  • LCD Screen display of Song

Advice for Future Students:

  1. Take some of the pre-requisites before taking this class. Perhaps some background E&M (physics 43), some coding experience (CS106AB), and some introductory electronic circuit class (Engr 40). If you like me, choose to dive into the class without these pre-reqs, be prepared to spend some extra time catching up with material and ready to settle for a humbler project.

  2. Make sure to explore the lab and all the components during the labs. I wish I had more time for this during lab sessions. For the final project you really need to be comfortable with all your components and soldering.

  3. Have fun and ask for help. Everybody this quarter, Wendy, Matt, David and student peers all have great advice about circuitry. Designing the final project is easier, quicker, and much more fun when it’s shared with others.

 

Comments (0)

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