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

Pranav Rajpurkar_FinalProject

Page history last edited by Pranav Rajpurkar 12 years, 8 months ago

For Whom?

This mp3 was made for me..

 

Need?

I needed a mp3 player which would be something that would be SSL(small, simple, lightweight)

I was in short of a music player which was unique yet maintained simplicity.

I had the need of having a small device that would be powered endlessly, a device I would be able to carry around easily.

 

Solution?

Make it...Small...Simple...Lightweight...

 These are the three words that describe what I expected to get out of my mp3 player.

 

How?

 

Planning Stage:

Perhaps the stage which requires most scratch paper work, and also the most fun stage.

 

 

Step by Step Approach

The brainstorming lead me to come up with a solid idea and understanding of my potential mp3. However, these ideas were to develop over time. Check out how much the initially planned and the later planned versions differ. I realized the step-wise refinement my brainstorming had brought me to. 

 

 

The schematics were very helpful in basing the product itself. Here's what it looks like:

Certainly a sweet device. This sweet device has many states that run it and it will be useful to draw a state diagram.

Tough decisions I had to face!

 

Battery vs USB power:

This was a very tough decision to make. While battery power added increased portability, it also added weight. Batteries also meant more hardware to be attached, which reduces simplicity of the mp3 player. A USB power seemed to be the better option to use power from.

 

Functionality vs Space:

The initial plans had the LCD as a part of the device. However, I was fighting for more space in the device to keep it as small as possible. Hence I chose to remove the LCD, and replace it with a label at the front.

 

Short term gain, long term trouble vs short term trouble, long term gain:

One of the major problems I had was that I had used thick wires to solder on parts to each other. I had already spent many hours soldering the parts together. However, I realized that I could instead use very thin wires. This would also mean needing to disorder the old wires and hence put into futility the hours put in. However, I decided that it was better to have a short term problem such as needing to resolder, rather than have a long term problem in the future. So I decided to rewire all of the components using thinner wires.

 

Now for the fun part:

DIY - my mp3 player

For this mp3 player, the real challenge lies in making the entire device as small as possible...and that requires some patience. Other than that, this project is very exciting and fruitful.

Step 1:

 

Get the parts linked on a breadboard. When finished lab 6 and able to play music, all you have to do is attach the potentiometer on pin A4, the interrupts on pins 6 and 7! The hardware..finitto. :D

 

Step 2:

Now that the hardware is finished...we need to be able to test the new volume control, next song and play/pause functionality we have added in. So we can begin to code:

 

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

int sensorPin = A4;    // select the input pin for the potentiometer

int sensorValue = 1020;  // variable to store the value coming from the sensor

int sensorValueMapped = 200;

int prevVolume =254;

boolean nextSongBoolean= false;

#define DEBUG // Comment this line to remove debugging features

 

 

 

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

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

 

#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

 

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

int mp3_vol    = 200;         // output volume range is 0 to 254 (0 = minimum, 254 = max)

 

volatile int isPlaying = HIGH;

  int count = 0;

int ListFiles(SdFile *dir) {

 

   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

    unsigned char pos = 0;

    for (unsigned char 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(" bytes 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 {

 

  if(nextSongBoolean==true){

    nextSongBoolean=false;

    break;

  } 

 

 

 if(isPlaying==HIGH ){

 volumeControl(); 

 

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

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

    }

  }

}

 

void setup() {

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

  pinMode(sensorPin, INPUT);

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

  digitalWrite(SS_PIN, 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 SD chipSelect pin to match your setup?");

    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 from your setup

  Mp3.volume(sensorValueMapped);             // default volume level is silent (note: 0 = minimum, 254 = max)

 

#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

  attachInterrupt(0, playPause, LOW);

    attachInterrupt(1, nextSong, LOW);

 dir_play(&root);

}

void playPause(){

   static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

   if (interrupt_time - last_interrupt_time > 200)

  {

   if(isPlaying ==HIGH){

    isPlaying = LOW;

  } else{

    isPlaying= HIGH;

}

  last_interrupt_time = interrupt_time;

}

 

 void nextSong(){

  static unsigned long last_interrupt_time = 0;

  unsigned long interrupt_time = millis();

   if (interrupt_time - last_interrupt_time > 200){

  nextSongBoolean = true; 

}  

  last_interrupt_time = interrupt_time;

 }

 

void loop() {

 dir_play(&root);

}

 

void volumeControl(){

 

  sensorValue = analogRead(sensorPin);

  sensorValueMapped  = map(sensorValue, 0 , 1020, 30,254);  

  // turn the ledPin on

  if(prevVolume!=sensorValueMapped){

   mp3_vol = sensorValueMapped;

   Mp3.volume(mp3_vol);

   prevVolume= sensorValueMapped;

}  

  }

 

Step 3:

Now that the code is done, we are all set...Well..almost...now comes the hard part. You will need to attach a very small piece of the perfboard to the Teensy and connect all the other parts directly on to the Teensy utilizing as little space as possible.

 

Make sure to use the right length and size of the wires.

 

Step 4:

Now that the mp3 parts have taken the small size, shown below, we can now start thinking about a cover, and a design for the cover.

I based my packaging on the idea that it was still supportive of the SSL(small.....) idea. It still needed to have a reasonable weight.

And after the packaging, there is only one last thing left..give your device a really cool name...like I have given mine "The PSR Player" where PSR stands for my full name. As soon as the naming is done...you are set to use your device.

 

You can now watch the video here:

Thanks,

Pranav Rajpurkar. Summer 2011. EE47

Comments (1)

Benjamin Tee said

at 5:56 pm on Aug 16, 2011

Nice job on making such a small player, despite all the challenges in integration.

We like that you had a brainstorming mind map and a nice visual design. We also like the iterations you went through to get to the final design. A paper prototype would have been a nice addition. It would be great for others to know what kind of parts you used, and how you assembled them(a pinout diagram). We understand it is hard to sketch something out, but a table like those in the labs would be helpful enough.

We wish that you had more of the circuit diagrams you used to assemble the player, state diagrams, and the thought process behind the design contraints, and what to avoid.

Overall, your persistence really paid off, and we hope you can continue to develop your player to become portable.

David, Akil and Ben

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