Final Project Doc


Final Project Documentation by Martin Sanner

Moddable MP3-Player

 

Partslist:

     -1 * Arduino Micro

     -1 * Arduino Uno

     -4 * small Pushbuttons

     -wiring :)

     -the LCD from Lab 3

     - an SD-Card-reader soldered for Breadborduse

     -an SD-Card-reader for PC-connection

     -2 micro-SD-cards

     -the SparkFun mp3 Shield: https://www.sparkfun.com/products/10628

     Addons:

     -an additional musical output

 

If one is thinking about digital design in combination with widely used electronical Devices, most people have a clear design in their heads.

Since most of these devices, namely Apples iPhone Series, as well as Google Android mobile Series, have a fixed Interface but such a widespread Usability, there's clearly a bottleneck in the design.

 

My intention was to invent a "user-proof", in other words, easy to use device in form of an mp3-Player, that can fix this issue.

Most of the technologies were already in use by the end of Lab #6, so i could draw on that knowledge, and expand to fit my needs.

 

But up to that point, the transformation of the initial idea to the finished product was long, and struck by change, since i did not have a design in mind, but rather a full fledged technology.

 

My initial Design was the following:

although it proved unreliable due to the sheer size of the MP3.

 

At the Point of designing the Physical Interface, most of the Software was already designed.

To create a viable Userinterface, i wrote a small Filemanagerclass, which can :

     A: handle most textdriven inputfiles

     B:dynamically allocate those files, and handle garbagecollection

     C:create Files in a userselected Datatype, which can be easily accustomed

     D:create new Files, read Files into a buffer, append new strings to the File and, on demand, delete the Files.

This class(interface and implementation) is to be found here:

 

          Filemanager.h:

          //
//  Filemanager.h
//  UTIL
//
//  Created by Martin Sanner on 05.08.13.
//  All rights reserved.

#ifndef __UTIL__Filemanager__
#define __UTIL__Filemanager__

#include <iostream>
#include <fstream>
#include "string.h"
#include <vector>


using namespace std;

class Filemanager{
public:
    void getFile();
    void getFile(string filename);
    void getFile(string Filename, bool write);
    
    Filemanager();
    ~Filemanager();
    Filemanager(string filename);
    Filemanager(string filename, int start);
    void readBuffer();
    void readFile();
    void readFile(string filename);
    void releaseFile(bool completeRelease);
    void writeFile();
    void writeFile(vector<string> input);
    void writeBuffer();
    void writeBuffer(vector<string> input);
    void clearBuffer();
    string getFiletype();
    void cutBuffer(int currentID);
    void setFilename(string name);
    vector<string> getBuffer();
    void setFileType(string type);
    void setFileType();
    void cutBuffer();
    string getFilename();
    
private:
    
    ofstream output;
    string FileType;
    string filename;
    ifstream input;
    vector<string> buffer;
    int Current;    //pointer to the current char
};
#endif /* defined(__UTIL__Filemanager__) */

 

     Filemanager.cpp

          //
//  Filemanager.cpp
//  UTIL
//
//  Created by Martin Sanner on 05.08.13.
//  Copyright (c) 2013 Hellixionmeta. All rights reserved.
//

#include "Filemanager.h"

void Filemanager::getFile(){
    string name;
    cout << "What is your desired Filename?";
    cin >> name;
    setFilename(name);
    getFile(filename);
}

void Filemanager::getFile(string Filename,bool write){
    setFilename(Filename);
    if(write){
        ofstream stream;
        stream.open(Filename);
        if(stream.is_open()){
        for(int bufpoint = 0;bufpoint <= buffer.size();bufpoint++){
            stream << buffer[bufpoint];
            stream << endl;
            }
            stream.close();
        }
    }
    else{
        readFile(Filename);
    }
};
void Filemanager::getFile(string Filename){
    setFilename(Filename);
   readFile(Filename);
};
void Filemanager::readBuffer(){
    for(int i = 0;i<=buffer.size();i++){
        cout << buffer[i]<<endl;
    }
};

Filemanager::Filemanager(){
    Current=0;
    output.open(filename);
    setFilename("");
    getFile(filename);
    FileType=getFiletype();
    clearBuffer();
    input.open(filename);
};
Filemanager::~Filemanager(){
    Current=0;
    FileType="";
    
    clearBuffer();
    input.close();
    
    
};
Filemanager::Filemanager(string Filename){
    filename=Filename;
    getFile(filename);
    setFileType();
    Current=0;
    clearBuffer();
    output.open(filename);
    input.open(filename);
};
Filemanager::Filemanager(string Filename,int id){
    filename=Filename;
    getFile(filename);
    Current=id;
    if(buffer.size()>Current){
          cutBuffer(Current);
    }
    
};
void Filemanager::readFile(){
    readFile(filename);
}
void Filemanager::readFile(string Filename){
   
    input=ifstream(Filename);
    
    string line;
    if(input.is_open())
    {
        while(!input.eof())
        {
            getline(input,line);
            buffer.push_back(line);
        };
    }else{
        cout << "Unable to open file:" << filename << endl;
    }
    
};
string Filemanager::getFiletype(){
    return FileType;
};
void Filemanager::setFileType(){
    if(getFilename().size()>4){
    FileType=getFilename().substr(filename.size()-3);
    }
};
string Filemanager::getFilename(){
    return filename;
}
void Filemanager::writeBuffer(vector<string> input){
    buffer = input;
}
void Filemanager::setFileType(string type){
    FileType=type;
    string temp=getFilename();
    temp=temp.substr(0,temp.size()-3);
    temp.append(FileType);
    setFilename(temp);
};
void Filemanager::setFilename(string name){
    filename=name;
}
void Filemanager::cutBuffer(int currentID){
        buffer.erase(buffer.begin(), buffer.begin()+buffer.size()/2);
};

void Filemanager::cutBuffer(){
    buffer.erase(buffer.begin(),buffer.begin()+buffer.size()/2);
}

void Filemanager::clearBuffer(){
    if(buffer.size()>0){
        for(long int i=buffer.size();i>=0;i--){
            buffer[i].erase();
        }
    }
};
void Filemanager::releaseFile(bool completeRelease){
    if(input.is_open()){
    input.close();
    }
    if(output.is_open()){
    output.close();
    }
    clearBuffer();
    if(completeRelease)
    {
        setFileType("");
        setFilename("");
    }
};
void Filemanager::writeBuffer(){
    bool isrunning=true;
    bool isHel;          //"*.hel is a custom Filetype, which is to be further expanded in future iterations. For Convenience, handle it as a textfile
    string answer;
    cout << "Please start writing to the Buffer now."<<endl << "If you want to end the writingprocess, please put in an emptyline:"<<endl;
    cout << "Do you want to use the .hel - datatype?[y/n]: "<<endl;
    cin >> answer;
    if(answer=="yes"){
        isHel=true;
    }else{
        isHel=false;
    }
    if(isHel){
        setFileType("hel");
    }
    string text;
    while(isrunning){
        cin >> text;
        if(text==""){
            isrunning=false;
        }
        buffer.push_back(text);
    }
};
void Filemanager::writeFile(){
    if(buffer.size() == 0){
    writeBuffer();
    }
    writeFile(buffer);
}
void Filemanager::writeFile(vector<string> input){
    if(buffer.size() == 0){
        writeBuffer();
    }
    if(filename==""){
        getFile();
    }
    getFile(filename);

    ofstream out;
    out.open(filename, ios::out | ios::binary);
    if(out.is_open()){
        if(FileType=="hel"){
            for(int i=0;i<=buffer.size();i+=2){
                out << buffer[i] << "" << buffer[i++] << endl;
            }
        }else{
            for(int i=0;i<=buffer.size();i++){
                out << buffer[i] << endl;
            }
        }
        out.close();
    }
}
vector<string> Filemanager::getBuffer(){
    return buffer;
};

 

          main.cpp:

               //
//  main.cpp
//  HELSetup
//
//  Created by Martin Sanner on 07.08.13.
//  Copyright (c) 2013 Hellixionmeta. All rights reserved.
//

#include <iostream>
#include "string"
#include "Filemanager.h"


using namespace std;

void help();

int main(int argc, const char * argv[])
{
    string file = "interface.txt";
    cout << "File: "<< file<<endl;
    help();
    Filemanager manager;
    bool isRunning = true;
    vector<string> temp;
    cout << "Start writing now."<<endl;
A:
    while(isRunning){
        string tempstring;
        cin >> tempstring;
        if(tempstring == "HELP"){
            help();
        }
        else if(tempstring == "STOP"){
            isRunning = false;
        }
        else{
            temp.push_back(tempstring);
        }
    }
    
    manager.writeBuffer(temp);
    manager.readBuffer();
    //The buffer gets doubled after reading from it. cutBuffer cuts the buffer in two equallysized halfs
    manager.cutBuffer();
    manager.getFile(file,true);
    manager.writeFile();
    cout << "Written to File."<<endl;
    //Check wether the User wants to finish
    cout <<  "are you sure you want to finish?[NO to repeat]"<<endl;
    string answer;
    cin >> answer;
    if(answer =="NO"){
        isRunning = true;
        cout << "You may start writing again."<<endl;
        goto A;          //Although this is considered bad style, it is IMO easier to use goto in that Situation with such a small program.
    }
    
    return 0;
};

void help()
{
    cout << "Please start putting in a set of Strings to be written to the file."<<endl
         << "To start a newline, please press enter. To stop writing, write 'STOP'."<<endl
         << "Write 'HELP' to view this command again."<<endl
         << "Please press enter now."<<endl;
};

 

To use the Class, simply take the .h File and the .cpp File, and drag them into a custom project.

Although this code was created and compiled using the xCode interface, it should work on any OS with a fully working C++ Compiler, since no System API is used whatsoever.

 

As instructed by the Program, Interfaces can be created by putting in Strings using the readBuffer() method in the class, which then were to be dynamically loaded by the arduino micro, and displayed on an lcd, while an arduino Uno plays the mp3-Files.

 

This process works as follows:

         MicroOS.ino:

          ///
/*This is the Arduino Micro program, which handles textoutput to the LCD, and reads the custom interface.txt file
 *
 *
 *
 *
 */
///
#include <SD.h>
#include <SdFat.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,5,4,3,2);

String[4] ReadFile(File file);
String* Interface;

//The Micro only needs 2 Buttons(next and last);
int ButtonNextID = 7;
int ButtonLastID = 6;

//acronyms
#define BNID ButtonNextID
#define BLID ButtonLastID

int currentID = 0;

void LCDRefresh(LiquidCrystal lcd);

void initPins();

bool isHigh(int Bid);
void modID(int currentID);

void setup(){
 
  lcd.begin(16,2);
  initPins();
   pinMode(10,OUTPUT);
  if(SD.begin(14)){
  File interface = SD.open("interface.txt",FILE_READ);
  Interface = ReadFile(interface);
       for(int i = 0; i <= 4;i++){
      String interface[i] = Interface[i];
      }
    }
  };



void loop(){
  LCDPrint(interface[currentID],lcd);
  modID(currentID);
  LCDRefresh(lcd);
};


void LCDPrint(String string, LiquidCrystal LCD){
  LCD.setCursor(0,1);
  LCD.display();
  LCD.println(string);
};

String[4] ReadFile (File file){
String[4] result;
int i = 0;
while(file.available()){
    result[i] = file.read
    i++;
  }
reeturn result;
};

isHigh(int Bid){
 return digitalRead(Bid) == HIGH;
}
void initPins(){
pinMode(10,OUTPUT);
pinMode(BLID,INPUT);
pinMode(BNID,INPUT);
}

void modID(int currentID){
  int lastStateBNID = digitalRead(BNID);
  int lastStateBLID = digitalRead(BLID);
  if(isHigh(BNID) && digitalRead(BNID) != lastStateBNID){
   currentID++;
  }
  if(isHigh(BLID) && digitalRead(BLID) != lastStateBLID){
  currentID--;
  }
 
     delay(250);
  lastStateBNID = digitalRead(BNID);
  lastStateBLID = digitalRead(BLID);
}

void LCDRefresh(LiquidCrystal lcd){
  lcd.noDisplay();
  delay(250);
}

 

    OS.ino:

          #include <SPI.h>
#include <SdFat.h>

#include <SFEMP3Shield.h>

#include <SD.h>


SdFat sd;
SFEMP3Shield MP3player;

int startButtonID = 5;
int StopbuttonID = 4;
int nextButtonID = 3;
int lastButtonID = 2;
const int NumTracks = 4;

void stopMusic();
void PlaylastTrack();
void PlayNextTrack();
void PlayPlaylist();

void setup() {

  Serial.begin(9600);
 
  //Buttoninterupts
  attachInterrupt(StopbuttonID,stopMusic,CHANGE);
  attachInterrupt(startButtonID,PlayPlaylist,CHANGE);
  attachInterrupt(nextButtonID,PlayNextTrack,CHANGE);
  attachInterrupt(lastButtonID,PlaylastTrack,CHANGE);  
  //start the shield
  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();
  Serial.println("starting");
  uint16_t currentVol = MP3player.getVolume();
  MP3player.setVolume(currentVol*2);
 
}

//do something else now
void loop() {
}
void StopMusic(){
  SFEMP3Shield Mp3Player;
    Mp3Player.stopTrack();
}

void PlayFirstTrack(){
  StopMusic();
  SFEMP3Shield Mp3p;
  Mp3p.playTrack(0);
}

void PlayLastTrack(){
 
  StopMusic();
  SFEMP3Shield Mp3P;
  Mp3P.playTrack(NumTracks);
}

void PlayPlaylist(){
  SFEMP3Shield Mp3P;
   for(int i = 0;i<=NumTracks;i++){
  Mp3P.playTrack(i);
  if(i == NumTracks){
      i = 1;
    }
  }
}

 

microOS.ino is the File to be uploaded to the Micro. If you have successfully finished Lab#6, there should be nothing of that needs further explanation in that File.

In case of the OS.ino, it is a quite different picture, since i found the following Library rather useful:

http://www.billporter.info/2012/01/28/sparkfun-mp3-shield-arduino-library/

This Library defines Methods and Classes for the MP3 Player in Conjunction either with the Arduino Mega or Uno.

These functions handle most of the code needed to play certain Files, and also allow for different Datatypes, like AAC and WAV.

 

Due to some adaptations in the Code, multiple redesigns were necessary to complete the Project, but in the end, it mostly worked out well, besides the fact, that the Arduino Micro does not have enough ram to dynamically allocate Memory and use the LCD, while the UNO has no problems whatsoever if the program is segregated enough.

 

Due to these Designchanges in the later history of the project, i wasn't able to complete any kind of Casing.

The Code on the other hand got a simplification from my initial Softwaredesign, with some ideas simplified.

 

When building the MP3 though, there was some struggle with the wiring, since most of the Micro-Pins were already used and, along with the MP3shield not comming with a viable option to add more wires to it, but besides that, it worked out OK(besides the RAM-problem :), so keep that in mind if you want to rebuild the MP3).

 

Since most of my design is targeted at the developers and creators POV, which was my initial idea, i think i reached that point very well, and is conform with my own POV.

 

As already mentioned, my timing wasn't as fortunate as it could have been, so my final Product is the following:

 

If you were able to follow my design and ideas so far, you should be good to go to reimplement my whole product in nearly no time, since i assume, that you might already have the knowledge needed from prior labs.

 

 

 

Martin Sanner