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

Richard Zajac's Press Play Final Documentation

Page history last edited by Richard Zajac 12 years, 8 months ago

Richard Zajac

Press Play EE-47

Final Project – Documentation

Project Abstract (Idea Summary)

 

I look at my project as a tribute. Over the past century, technology has evolved from a strictly mechanical field to today’s hyper-advanced nano-scale technological state. Along the way, devices have been invented, platforms and gadgets have been made obsolete, and brands have risen and fallen. By incorporating many past devices into a new one, I am not only able to breathe new life into old technology, but also able to create a new product that didn’t even exist before.

 

By combining a multimeter (for volume readout), a Nokia 5110 telephone, (ad a display) a headphone port from an old iBook G3, a discarded solar panel, an Apple G4 cube, (and its capacitive buttons) alongside my code, an MP3 decoder, a Teensy 2.0 running Arduino software, I was able to create an MP3 player to evoke the lore of computing days gone by.

 

The birth and life of the BoomCube G4:

The idea started simultaneously with an LCD and a broken Apple PowerMac G4 Cube. After viewing Prof. Sirkin’s in-class presentation, I realized that the G4 Cube might be a probable candidate to house an enclosure, especially when I framed it as a way to improve upon what was in many ways a ‘failed design’

 

The above figure (from Slide Show) was my inspiration for my housing.

 

The next class, the idea for my display came to me,

 

Above are the innards of a Nokia 5110, including the LCD, which I would find in the form of a broken cell phone at WierdStuff in Sunnyvale.

 

For any future students reading this, Wierdstuff was a fantastic resource for a passionate hardware hacker such as myself, and is located at 384 W. Carribean Avenue, Sunnyvale CA 94089

 

 

 

This is a picture I took, which was my first impression of the inside of this technological recycling garage.

 

 

Post-Inspiration: The build (but planning first)

 

With all my key parts in tow, I began to construct the actual MP3 player. Before I could do that, however I had to prototype the actual design and sketch my Verplank diagram to thoroughly think through the execution of what I was doing.

 

 

Above is my Verplank diagram wherein I effectively vetted the idea of using recycled components of broken, poorly designed, or obsolete parts into a new, intuitive creation.

This was my paper prototype of my BoomCube G4, complete with my 3 buttons on top: play-pause, rewind, and fast forward, and the SD slot. The accompanying photos also show the back where the solar panel I used was mounted, and the 3 ports on the back were installed. One notable exception was the multimeter on the front; I just absolutely had to get the real thing in my hands to work up a way to mount it.

 

The (Real) Build

 

This is where much of the magic happened. I started by attacking the multimeter I was to use. This device printed out the readouts at close intervals, but as soon as I had it apart, I discovered a problem…

 

The markings inside this multimeter readout indicated that the printer-tractor only spun at 1/8 inch an hour, far too slow for any practical and/or intuitive volume read out use. I decided to gut the multimeter, and not use the printing capability. Ultimately, the multimeter would simply bounce to the music, but not without some mods and additions on my part.

 

 

 

Above was the original, 1940s era thermoplastic faceplate for the multimeter. I decided to make one of my own. Something analogous to the restomod process in the automotive world. (http://www.google.com/search?q=define%3Arestomod)

With Marla’s help, I used a compass and protractor to deal with the difficult hole in the corner that would accommodate the OEM knob, and set to laser cutting.

 

Watch my laser cutting video here:

http://www.youtube.com/watch?v=QWdqenJ9Lls

 

This was near to the completion of the Laser Cutting process.

 

After completing my multimeter build, I spray painted the back black, and epoxied the new faceplate to the multimeter, which I should also mention blocked the hole for a scroll wheel I no longer needed in this paperless design.

 

Next was the LCD, the Nokia.

 

I took the phone, and gutted the innards. After excitedly running about the lab showing my new (broken) phone with a 5110 LCD in it, I proceeded to install the breakout board. It didn’t fit. After shaving the edges of the break-out board it did fit, and I painstakingly screwed the phone back together. I then proceeded to mount a ribbon cable to the headers on the breakout board, then hot glue the phone into what was now to be its display cradle.

 

 

After a quick run of the demo software, I confirmed that my Nokia 5110 could indeed be talked to by the Teensy.

 

The next big step was the top control panel; I made a breakout board for the buttons which I used to control the rewind, fast-forward, and play-pause functions. This would also house the SD card reader/adapter.

 

I began in Illustrator, cutting a circle in half with a rectangle in between to replicate the previous vent hole of the G4 cube. I used the white liner of the cube’s top as a template for which to confirm my design dimensions by, shown here:

 

I used the centimeter-marked background to further confirm my design.

 

I then proceeded to cut thick, half-inch acrylic for the button pad. Realizing that I had already made the breakoutboard which held all the buttons on headers, I used an online web site which told me the spacing between holes on 98% of perf-boards to calculate the distance between the buttons. The actual size for the button’s holes came from the manufacturer’s spec sheet: found here:

http://www.sparkfun.com/datasheets/Components/General/TSL12121.pdf

 

Speaking of which, here is the link to the actual buttons used which are normally clear, but glow a brilliant blue when power is applied, complimenting the Apple logo on the cube.

http://www.sparkfun.com/products/10443

 

This was a previous button-pad design that Marla and I had drawn up using an iPod click wheel, but it was deemed (by me, and with peer confirmation) too complex both for the builder and the end user.

 

 

 

Finally, A look at some of the prototyping process:

 

Here’s some of the early-stage cubefitting

 

This (below) was my project when I had it working at the breadboard stage:

 

 

Then I built it at the perf-board stage, with my final button design, rather than the rotary encoder based design shown above, (I’d breadboarded that too, but did not snap a pic of exactly that stage)

 

Unfortunately, this first perfboard was too unreliable, with several flaky connections.

 

Though it was a difficult mental decision, I went ahead and rebuilt the entire perf-board, which was in hindsight a great choice. With David’s help, I placed the wires on a side opposite the soldering this time, and left extra room between components knowing I did not have extreme space constraints compared with some of the other projects shown this season.

Here she is: my pride and joy, the properly soldered perf-board. Don’t mind the pressure sensor; it just got swept up by some of the tape holding my connections that I was finishing.

 

Thanks so much for reading and watching, and it is now that I invite you, the reader, to peruse my code, and enjoy the video of my Final Project posted at the very bottom

 

Best,

Richard

 

 

Final Code:

 

MP_Final.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)

* Modified for use with the Teensy 2.0 by Akil Srinivasan (akils@stanford.edu)

*

* Crafted into an engine for the BoomCube G4 by Richard Zajac

*

*/

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

#include <CapSense.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

boolean playing = true;

 

int volPin = 17;

String songList[MAX_FILE_COUNT];

int songpos = 0;

SdFile dataFile;

int songCount = 0;

 

 

#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

#define mp3_vol 220 // output volume range is 0 to 254 (0 = minimum, 254 = max)

 

int sensorPin = A8; // 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;

boolean lastSongBoolean=false;

volatile int isPlaying = HIGH;

int index = 0;

int numFiles;

 

 

 

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

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;

}

 

void previousSong(){

static unsigned long last_interrupt_time = 0;

unsigned long interrupt_time = millis();

if (interrupt_time - last_interrupt_time > 200){

lastSongBoolean = true;

}

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

}

 

/*

* 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(lastSongBoolean){

lastSongBoolean = false;

songpos = songpos - 2;

break;

}

if(isPlaying == HIGH ){

volumeControl();

bytes_to_read = file->read(bytes, read_buffer);

Mp3.play(bytes, bytes_to_read);

}

}

while (bytes_to_read == read_buffer);

#ifdef DEBUG

Serial.println("Played song...");

#endif

}

 

 

void volumeControl(){

sensorValue = analogRead(sensorPin);

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

// turn the ledPin on

if(prevVolume != sensorValueMapped){

Mp3.volume(sensorValueMapped);

prevVolume = sensorValueMapped;

}

}

 

 

/*

* 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((index*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.clear_buffer();

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

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

// attachInterrupt(0 ,press ,FALLING);

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

 

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

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

Serial.println(String(songList[i]));

}

attachInterrupt(1, playPause, LOW);

attachInterrupt(2, nextSong, LOW);

attachInterrupt(3, previousSong, LOW);

while(true){

songpos = 0;

char* songName = &songList[songpos][0];

playSong(&root);

if(songpos<numFiles){

songpos++;

}

else{

songpos = 0;

}

root.close();

}

}

 

// Do nothing for now

void loop() {

while (1);

}

 

dir_read.pde

 

void dir_read (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.clear_buffer();

// mp3_play(&dataFile);

songList[songpos]=fn;

songpos++;

songCount++;

}

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

}

}

}

 

 

Finally, here’s a link to my final video presentation here:

 

http://www.youtube.com/user/rickyzstl#p/u

 

Last notes:

For anyone not in this class, but enjoying perusing the projects, this was the lab (after some light clean up) where this was all performed.

 

I would like to recognize a certain student, Daniel Zhang who was instrumental in helping nearly every student in the lab, so far as I could tell, when things went amiss or when boards would fry. Daniel, I hope you get the recognition you deserve.

 

Lastly a special and very heartfelt ‘Thanks’ To Prof. David Sirkin, TA’s Ben Tee and Akil Sirinivasan for their incredible help in the lab and relentless grading work and wiki updating. The class was truly a phenomenal experience that provided us with a skill set that was, to my knowledge, previously unavailable to the high school level, which is where I was at when I took the course (Rising Senior). Thanks again to all, and to all a good lab!

 

Comments (1)

David S said

at 3:18 pm on Aug 16, 2011

Nice job hacking together weird stuff Richard! That's totally the spirit of the class. :-)

We like that you included a clear point of view (although granted, it was based more on interests than on user needs), Verplank diagram and made a paper prototype. We also like your narrative, that walks the reader through your inspiration, parts acquisition, and assembly. We realize that it's difficult to do much prior sketching and design for a project like this, but still, more evidence of purposeful design is always welcome. We also also like that you included links to the store where you found your components. That may be very helpful to future students.

We wish you had included a bit more detail regarding your circuitry. Where did you get the Nixie Tube (for those interested, visit: http://en.wikipedia.org/wiki/Nixie_tube), how did you connect it, and did you have to amplify your audio signal (and if so, did you use an op-amp, transistor, or amplifier chip) or change the resistors in the voltmeter in order to drive its needle? We also wish that your document included more detail about connections between your components, and that your video included more close-ups of the voltmeter (and showing it moving would have been even better!).

Overall, a well-integrated project, done with the eagerness and enthusiasm that we've come to expect from you. Most appreciated...

David, Akil and Ben

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