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”.

 

DSC04004

Picture 1 of 21

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 rewinds. 
* Button 12 plays the next musicfile
* of the directory.
*
* Feel free to use the software on your own risk. If you extend the software 
* i would be happy to get your code. Inform me at koelnerwasser.de
*
*/

#include <Adafruit_VS1053.h>
#include <SD.h>
#include <SPI.h>

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


const int maxFilesInDir = 50;

// list of played files for the rewind function
char listOfLastPlayedFilesInDir [maxFilesInDir][20];
int countOfEntryInLastPlayedArr = 0;

// 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);
        // store it for the rewind function
        sprintf (listOfLastPlayedFilesInDir[countOfEntryInLastPlayedArr], "%s", actualMusicFileName);
        countOfEntryInLastPlayedArr++;
        
        // 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);
              countOfEntryInLastPlayedArr = 0;
              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();
}






 

Download the code and the library here: ABox.zip

 

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