Arduino Musicplayer for my daughter

A while ago I wanted to buy a musicplayer for my daugther. The first idea was to buy one of these “pinky” plastic things made in east asia. While thinking about the lifetime of these  plastic players in a 2 years old child hand, I decided to look for alternatives. Then I found the famous hoerbert which is hand made in Germany, with responsible ingredients and really suitable for a child. The price is not cheap but really fair, as I know after building my version of a child capable music player. Inspired by the hoerbert I decided to build my own version called ABox because I want to have some changes:

  • run with an rechargeable pack instead of batteries
  • easy upload of songs with any format without the need of conversion
  • “stereo” Sound

For using standard audiofiles I needed to build a stereo musicplayer anyway. To have a good sound with a mono device, the music must be compressed to one channel. this means each file must be converted before uploading it to the players SD card.

Like most of my small diy projects i decided to use an arduino as base for the player. I combined it with adafruits mp3 shield and a self welded button panel. The 12 button panel works with 11 resistors which are bridged if a button is pressed. With this technique its possible to measure the voltage on one of the arduinos analog ports and to decide which button was pressed. A circuit plan can be found here or by searching for “arduino button keyboard resistor”.

AdaBox

The concept of operation is easy: 10 Buttons represent a folder/playlist on the SD card, Button 11 jumps back, Button 12 jumps forward. If the AdaBox is switched off, it stores the actual played file on the sd card. If it’s switched on, it starts playing with the last file.

Here’s the code:

 
/*
* Software for the ABox - a Hoerbert inspired music player for children.
* Uses the Adafruit SD MP3 Player. The buttonboard is a 12 button board 
* which uses one of the analog ports and 1/4W 3,3K do divide the voltage
* if a button is pressed. For more infos search for "arduino button analog resitor"
* 
* Usage of the player: The SD Card must contain 10 Directories with music files. 
* Button 1-10 starts playing the music in the correspondent file. Button 11 starts 
* playing the actual directory from the beginning. Button 12 plays the next musicfile
* of the directory.
*
* Feel free to use the software on your own risk. If you extend the software especially the
* rewind function i would be happy to get your code. Inform me at koelnerwasser.de
*
*/

#include 

#include 
#include 

//## user preferences
const char lastPlayedStorageFile[] = "LASTFILE.TXT";


//## programm variables
// buttonpad
//last pressed button
int actualButtonId = 99;
bool firstRun = true;

const int pinPadPin = A3;

// music dir/file
char actualMusicDir[5];
char* actualMusicFileName;
char actualMusicFileFullPath[20];
char lastSessionMusicFilePath[20];


int maxFilesInDir = 80;



// volume
const int volumePotiPin = A2;



// Mp3 Board
#define BREAKOUT_RESET  9      // VS1053 reset pin (output)
#define BREAKOUT_CS     10     // VS1053 chip select pin (output)
#define BREAKOUT_DCS    8      // VS1053 Data/command select pin (output)
// These are the pins used for the music maker shield
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)
// These are common pins between breakout and shield
#define CARDCS 4     // Card chip select pin
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin

// create MusicPlayer object
Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);



void setup() {
  //Serial.begin(9600);

  Serial.println("Adafruit VS1053 Library Test");

  // initialise the music player
  if (! musicPlayer.begin()) {
    Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
    while (1);
  }
  Serial.println(F("VS1053 found"));

  // generate Testtone
  // musicPlayer.sineTest(0x44, 500);

  // we need bass, bass
  // bass values
  byte bassAmp = 9; // value should be 0-15
  byte bassFrq = 9; // value should be 0-150

  // treble values
  byte trebAmp = 1; // value should be 0-15
  byte trebFrq = 1;

  // convert values into ushort for cpu command
  unsigned short bassCalced = ((trebAmp  & 0x0f) << 12)
                              | ((trebFrq  & 0x0f) <<  8)
                              | ((bassAmp  & 0x0f) <<  4)
                              | ((bassFrq  & 0x0f) <<  0);   // process bass cpu command   musicPlayer.sciWrite(0x02, bassCalced);   if (!SD.begin(CARDCS)) {     Serial.println(F("SD failed, or not present"));     while (1);  // don't do anything more   }   Serial.println("SD OK!");   // list files   // printDirectory(SD.open("/"), 0);   // define interrupt  - Pininterrupt   if (! musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT))     Serial.println(F("DREQ pin is not an interrupt pin")); } void loop() {   // Set volume   // lower numbers == louder volume!   int volume = getVolumeValue();   musicPlayer.setVolume(volume, volume);   File actualDirPointer;   File actualFilePointer;   // ## inital start: nothing pressed, read stored last used file and play it ##   if (firstRun) {     // read last played file     readLastSessionFilenameFromSd();     if (sizeof(lastSessionMusicFilePath) > 0) {
      //Serial.print(dumpCharArray(lastSessionMusicFilePath,sizeof(lastSessionMusicFilePath)));

      if (SD.exists(lastSessionMusicFilePath)) {
        Serial.println(F("Found LastPlayed Musicfile of Last Session on SD, try to initalize variables for playing it"));
        // extract dir
        char *ptrOfSlashOccurence = strchr(lastSessionMusicFilePath, '/');

        // set dir and file pointer to the correct dir
        if (ptrOfSlashOccurence) {
          // extract dir and file string
          int indexOfSlash = ptrOfSlashOccurence - lastSessionMusicFilePath;
          strncpy(actualMusicDir, lastSessionMusicFilePath, indexOfSlash + 1);
          char lastSessionMusicFileName[15];
          strncpy(lastSessionMusicFileName, lastSessionMusicFilePath + indexOfSlash + 1, sizeof(lastSessionMusicFilePath));

          // set filepointer
          actualFilePointer = SD.open(lastSessionMusicFilePath);
          // set dirpointer
          actualDirPointer = SD.open(actualMusicDir);

          // forward dir pointer to lastSessionMusicFilePath
          int breaker = 0;
          File toCompare;
          while (breaker < maxFilesInDir) {
            breaker++;
            toCompare = actualDirPointer.openNextFile();
            if (strcmp(toCompare.name(), lastSessionMusicFileName)  == 0 ) {
              toCompare.close();
              break;
            }
            toCompare.close();
          }
          if (breaker < 50) {             Serial.println(F("Initalize of variables for playing lastSession Music File successful"));             firstRun = false;             goto lastSessionStart;           }         }       }     }     Serial.println(F("Did not found the file on sd or unable to init the variables"));     firstRun = false;   }   // jumpPoint: playlistelected goto   playlistSelected:   // ## main entry ##   if (checkButton()) {     // check if a playlist was selected     if (actualButtonId > 0 && actualButtonId < 11) {       // open the dir       sprintf (actualMusicDir, "%i/", actualButtonId);       actualDirPointer = SD.open(actualMusicDir);       Serial.print(F("Actual Dir: "));       Serial.println(actualMusicDir);       // play selected playlist until no more files to play       while (true) {                  // jumpPoint: forward goto         forward:         // reset actual buttonId for detect a new buttonPress         // - the selected value is not longer needed         actualButtonId = 99;         // get file from directory handle         actualFilePointer = actualDirPointer.openNextFile();         if (!actualFilePointer) {           Serial.println(F("Last File of dir reached.. Starting at the beginning of the actual dir"));           actualDirPointer.rewindDirectory();           actualFilePointer = actualDirPointer.openNextFile();         }         Serial.print(F("Actual File: "));         Serial.println(actualFilePointer.name());         //Ignore underscore prefix "._ " Files of OSX which are delivered by openNextFile()         if (strchr(actualFilePointer.name(), '_')) {           int indexOfUnderScore = strchr(actualFilePointer.name(), '_') - actualFilePointer.name();           if (indexOfUnderScore == 0) {             Serial.println(F("Filename starts with underscore, ignoring it because its a OSX Filesystem file"));             actualFilePointer = actualDirPointer.openNextFile();             if (!actualFilePointer) {               Serial.println(F("Last File of dir reached.. Starting at the beginning of the actual dir"));               actualDirPointer.rewindDirectory();               actualFilePointer = actualDirPointer.openNextFile();             }           }         }         // jumpPoint: start with last session file         lastSessionStart:         // no file found         if (!actualFilePointer) {           Serial.println(F("No Files/Dir found goto playlistSelected"));           goto playlistSelected;         }         // build filename to play         actualMusicFileName = actualFilePointer.name();         sprintf (actualMusicFileFullPath, "%s%s", actualMusicDir, actualMusicFileName);         Serial.print(F("Created full Filepath: "));         Serial.println(actualMusicFileFullPath);         // store last played filename, for use as initial file after restarting the system         // store before playing because SD Lib cant not read and write at one time..         saveFilenameOnSd(actualMusicFileFullPath);         // play         //dumpCharArray(actualMusicFileFullPath,sizeof(actualMusicFileFullPath));         if (! musicPlayer.startPlayingFile(actualMusicFileFullPath)) {           Serial.println(F("Could not open file"));           musicPlayer.reset();           goto forward;         }         else {           Serial.print(F("Started playing:"));           Serial.println(actualMusicFileFullPath);         }         // ## check button pressed button while playing ##         while (musicPlayer.playingMusic) {           // set volume while playing           int volume = getVolumeValue();           musicPlayer.setVolume(volume, volume);           // check for changed playlist, forward or stop           if (checkButton()) {             actualFilePointer.close();             //Serial.print(F("Actual Button ID:"));             //Serial.println(actualButtonId);             // backward             if(actualButtonId == 12){               Serial.println(F("Backward pressed"));               musicPlayer.stopPlaying();               musicPlayer.softReset();               actualDirPointer.rewindDirectory();               delay(300);               goto forward;             }             // forward             else if (actualButtonId == 11) {               Serial.println(F("Forward pressed"));               musicPlayer.stopPlaying();               musicPlayer.softReset();               actualFilePointer.close();               delay(300);               goto forward;             }             // playlist             else if (actualButtonId > 0 && actualButtonId < 11 ) {               musicPlayer.stopPlaying();               musicPlayer.softReset();               actualFilePointer.close();               Serial.println(F("Playlist selected"));               delay(300);               goto playlistSelected;             }           }           //delay(300);         }         musicPlayer.reset();         actualFilePointer.close();       }     }   } } // FUNKTION get value of the volume poti int getVolumeValue() {   // read PotiData   int vol = analogRead(volumePotiPin) ; // 0-1023   // Serial.println("poti Value:");   // Serial.println(v);   // value correction   vol = vol / 15;   // limit max volume 0 => max vol
  if (vol < 30) {
    vol = 30;
  }
  // Serial.println("Volume Value:");
  // Serial.println(v);
  return vol;

}

// FUNKTION File listing helper
void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      //Serial.println("**nomorefiles**");
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {       Serial.print('\t');     }     Serial.print(entry.name());     if (entry.isDirectory()) {       Serial.println("/");       printDirectory(entry, numTabs + 1);     } else {       // files have sizes, directories do not       Serial.print("\t\t");       Serial.println(entry.size(), DEC);     }     entry.close();   } } // FUNKTION check button boolean checkButton() {   int buttonTmp = readPressedButtonId();   if (buttonTmp > 0 && buttonTmp < 13)   {     if (actualButtonId != buttonTmp) {       actualButtonId = buttonTmp;       return true;     }     else     {       return false;     }   } } // FUNKTION read Keypad int readPressedButtonId() {   long button = 0;   long voltage = 501;   voltage = analogRead(pinPadPin);   /*   String dbg = "Volt aktuell: ";   dbg += voltage;   Serial.println(dbg);   */   if (voltage > 850) {
    button = 12;
  }
  else if (voltage > 790) {
    button = 1;
  }
  else if (voltage > 710) {
    button = 2;
  }
  else if (voltage > 640) {
    button = 3;
  }
  else if (voltage > 560) {
    button = 4;
  }
  else if (voltage > 540) {
    button = 5;
  }
  else if (voltage > 490) {
    button = 99;
  }
  else if (voltage > 460) {
    button = 10;
  }
  else if (voltage > 420) {
    button = 9;
  }
  else if (voltage > 360) {
    button = 8;
  }
  else if (voltage > 300) {
    button = 7;
  }
  else if (voltage > 220) {
    button = 6;
  }
  else if (voltage > 120) {
    button = 11;
  }

  /*
  dbg = "Knopf: ";
  dbg += button;
  dbg += " gedrueckt";
  Serial.println(dbg);
  */

  return button;
}

// FUNKTION safe given filename into a file  on the sd card
void saveFilenameOnSd(char* fileNameToStore) {

  File fileToWrite = SD.open(lastPlayedStorageFile, FILE_WRITE);

  if (fileToWrite) {
    fileToWrite.seek(0);
    fileToWrite.println(fileNameToStore);

    // close the file:
    fileToWrite.close();
    Serial.print(F("Saved given Filename to SD Card:"));
    Serial.println(fileNameToStore);
  } else {
    // if the file didn't open, print an error:
    Serial.print(F("SaveFilenameOnSd: error opening File:"));
    Serial.println(lastPlayedStorageFile);
  }
}


// FUNKTION read last played filename stored in file from sd card
void readLastSessionFilenameFromSd() {

  File fileToRead = SD.open(lastPlayedStorageFile);

  if (fileToRead) {

    int i = 0;
    int maxRead = sizeof(lastSessionMusicFilePath);
    while (fileToRead.available() && i < maxRead - 1) {
      lastSessionMusicFilePath[i] = fileToRead.read();

      // change ascii 13 from fileend to string end
      if (lastSessionMusicFilePath[i] == '\r') {
        lastSessionMusicFilePath[i] = '\0';
        break;
      }
      i++;
    }

    // close the file:
    fileToRead.close();
    Serial.print(F("Read saved Filename from SD Card:"));
    Serial.println(lastSessionMusicFilePath);

  } else {
    // if the file didn't open, print an error:
    Serial.print(F("getSavedFilenameFromSd: error opening File:"));
    Serial.println(lastPlayedStorageFile);
  }
}

void dumpCharArray(char* charArrayToDump, int lengt) {

  for (int i = 0; i < lengt; i++)
  {
    Serial.print(F("["));
    Serial.print(i);
    Serial.print(F("] is {"));
    Serial.print(charArrayToDump[i]);
    Serial.print(F("} which has an ascii value of "));
    Serial.println(charArrayToDump[i], DEC);
  }
  Serial.println();
}

 

Here is the list of the used parts:

  • Arduino UNO ( I used a  genuine Arduino )
  • Adafruits “Music Maker” MP3 Shield
  • 12 Resistors 1/4W 3,3KΩ
  • NiMh Rechargeable-Pack PP1 7,2V 1,1A
  • 1 Universal Circuit Board H25PS160
  • 3 Colored Button  Cap 1D00
  • 3 Colored Button  Cap 1D04
  • 3 Colored Button  Cap 1D06
  • Colored Button  Cap 1D08
  • 1 Potentiometer PO6M-LIN 22KΩ
  • 12 Pushbutton 3FTL6
  • 2 Speaker Visatone VIS FR 10HM-8  8Ω
  • an old USB female connector (only used as connector for the charging current)
  • a big metal on/off switch like HS 631 H2
  • a wooden storage box from the diy store as base fore the housing

The rechargeable pack is directly connected with to contacts of the misused USB connector. Actually I use an old USB cable to connect to the recharger. Here are some more pictures of the build process:

123

4

5

42

641

7 9   810    11

 

 

 

 

Vermurkstes Rahmendesign

Seit Tagen versuche ich ein Rahmendesign für meinen ersten selbstgebauten Rahmen zustande zu bringen. Da es nicht wirklich klappt, habe ich die Fabulösen Selberbruzzler in Ihrem Forum um um Rat gebeten. Da das Bikeboard kein Dateideiupload erlaubt habe ich mein Raddesign hier hochgeladen. Es wurde mit dem ebenfalls Fabulösen RattleCAD erstellt und das ich damit nicht klarkomme liegt aktuell eher an meinem Unverständnis als am Programm.. Hier die RattleCAD XML Datei: dawa_nabenschaltung.xml