Saturday, April 4, 2015

Audino - Arduino MP3-Player (17) - Sketch 3 (Der komplette Code)

Nachdem die letzten beiden Beiträge den Sketch behandelt und ausführlich erklärt haben, ist hier nochmal der ganze Quellcode dargestellt, der auf das Arduino-Board gespielt wird.
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
 
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)
#define DREQ          3      // VS1053 Data request, ideally an Interrupt pin
#define CARDCS        4      // Card chip select pin
Adafruit_VS1053_FilePlayer musicPlayer =
    Adafruit_VS1053_FilePlayer(SHIELD_CSSHIELD_DCSDREQCARDCS);
 
// VS1053 play speed parameter
#define para_playSpeed 0x1E04
 
// constants won't change
 
// the number of the pin that is used for the pushbuttons
const int buttonsPin = A0;
 
// the pin of the potentiometer that is used to control the volume
const int volumePin = A1;
 
// wait before next click is recognized
const int buttonPressedDelay = 1000;
 
 
// variables will change
 
byte currentFolder = 1;
unsigned int currentFile = 0;
unsigned int numberOfFiles[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
// the current volume level, set to min at start
byte volumeState = 254;
 
// last button that was pressed
byte lastPressedButton = 0;
// is the last pressed button released
boolean released = true;
// remember if the back button was pressed last time
byte lastReleasedButton = 0;
// the time at the back button was pressed last time
long lastBackButtonTime = 0;
 
char currentTrackFileName[] = "/0/current.txt";
 
// the setup routine runs once when you turn the device on or you press reset
void setup()
{
    // disable LED L
    pinMode(13, OUTPUT);
    digitalWrite(13, LOW);    
 
    // initialize serial communication at 9600 bits per second
    //Serial.begin(9600);
 
    // initialise the music player
    if (!musicPlayer.begin())
    {
        //Serial.println("VS1053 not found");
        while (1);  // don't do anything more
    }
 
    // initialise the SD card
    SD.begin(CARDCS);
 
 
    // If DREQ is on an interrupt pin (on uno, #2 or #3) we can do background
    // audio playing
    musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
 
    musicPlayer.sineTest(0x44, 100);    // Make a tone to indicate VS1053 is working 
 
    // read the number of tracks in each folder
    for (byte i = 0; i < 10; i++)
    {
        String temp = "/";
        temp.concat(i);
        char filename[3];
        temp.toCharArray(filename, sizeof(filename));
        numberOfFiles[i] = countFiles(SD.open(filename));
        //Serial.print(filename);
        //Serial.print(": ");
        //Serial.println(numberOfFiles[i]);
    }
 
    // read remembered track
    if (SD.exists(currentTrackFileName))
    {
        File file = SD.open(currentTrackFileName, FILE_READ);
        if (file)
        {
            currentFolder = file.readStringUntil('\n').toInt();
            currentFile = file.readStringUntil('\n').toInt() - 1;
        }
        file.close();
    }
 
 
    delay(100); // init delay
}
 
 
// counts the number of files in directory
unsigned int countFiles(File dir)
{
    unsigned int counter = 0;
    while (true)
    {
        File entry = dir.openNextFile();
        if (!entry)
        {
            // no more files
            break;
        }
 
        counter++;
        entry.close();
    }
    dir.close();
 
    return counter;
}
 
// the loop routine runs over and over again forever
void loop()
{
    // play next song if player stopped
    if (musicPlayer.stopped())
    {
        playNext();
    }
 
    // check the volume and set it
    checkVolume();
 
    // check if a button is pressed and perform some action
    checkButtons();
 
    delay(1); // delay in between reads for stability
}
 
 
// checks the value of the potentiometer
// if it has changed by 2 then set the new volume
void checkVolume()
{
    // read the state of the volume potentiometer
    int read = analogRead(volumePin);
 
    // set the range of the volume from max=0 to min=254
    // (limit max volume to 20 and min to 60) 
    byte state = map(read, 0, 1023, 20, 60);
 
 
    // recognize state (volume) changes in steps of two
    if (state < volumeState - 1 || state > volumeState + 1)
    {
        // remember the new volume state
        volumeState = state;
 
        // set volume max=0, min=254
        musicPlayer.setVolume(volumeState, 254);
 
        // print out the state of the volume
        //Serial.print(volumePin);
        //Serial.print(" volume ");
        //Serial.println(volumeState);
    }
}
 
// check if some button is pressed
// play first track, if button is not pressed last time
// play next track, if a button is pressed again
void checkButtons()
{
    // get the pressed button
    byte pressedButton = getPressedButton();
 
    // if a button is pressed
    if (pressedButton != 0)
    {
        //Serial.print("Taste: ");
        //Serial.println(pressedButton);
 
        // if a track/play list button is pressed
        if (pressedButton < 10 && released)
        {
            musicPlayer.stopPlaying();
            if (currentFolder == pressedButton)
            {
                playNext();
            }
            else
            {
                currentFolder = pressedButton;
                currentFile = 1;
                playCurrent();
            }
 
        }
        // if a function button is pressed
        else
        {
            if (pressedButton == 10 && released)
            {
                musicPlayer.stopPlaying();
                long time = millis();
 
                // this is the second press within 1 sec., so we 
                // got to the previous track
                if (lastReleasedButton == 10 && 
                    ((time - lastBackButtonTime) < buttonPressedDelay))
                {
                    playPrevious();
                }
                else
                {
                    playCurrent();
                }
                lastBackButtonTime = time;
            }
            else if (pressedButton == 11 && released)
            {
                // increase play speed
                musicPlayer.sciWrite(VS1053_REG_WRAMADDRpara_playSpeed);
                musicPlayer.sciWrite(VS1053_REG_WRAM, 3);
                //Serial.println("increase speed");
            }
        }
 
        released = false;
        lastReleasedButton = pressedButton;
    }
    else
    {
        released = true;
 
        // reset play speed
        if (lastPressedButton == 11)
        {
            musicPlayer.sciWrite(VS1053_REG_WRAMADDRpara_playSpeed);
            musicPlayer.sciWrite(VS1053_REG_WRAM, 1);
        }
    }
 
    // remember pressed button
    lastPressedButton = pressedButton;
}
 
 
void playPrevious()
{
    currentFile--;
    if (currentFile < 1)
    {
        currentFile = numberOfFiles[currentFolder];
    }
    playCurrent();
}
 
void playNext()
{
    currentFile++;
    if (currentFile > numberOfFiles[currentFolder])
    {
        currentFile = 1;
    }
    playCurrent();
}
 
void playCurrent()
{
    if (numberOfFiles[currentFolder] > 0)
    {
        rememberCurrentTrack();
 
        String temp = "/";
        temp.concat(currentFolder);
        temp.concat("/");
        temp.concat(currentFile);
        temp.concat(".mp3");
        char filename[temp.length() + 1];
        temp.toCharArray(filename, sizeof(filename));
        musicPlayer.startPlayingFile(filename);
 
        //Serial.print("Play ");
        //Serial.println(filename);
    }
}
 
void rememberCurrentTrack()
{
    if (SD.exists(currentTrackFileName))
    {
        SD.remove(currentTrackFileName);
    }
 
    File file = SD.open(currentTrackFileName, FILE_WRITE);
    if (file)
    {
        file.println(currentFolder);
        file.println(currentFile);
    }
    file.close();
}
 
 
// returns 0 if no button is pressed,
// else the number of the pressed button is returned (1 - 11)
byte getPressedButton()
{
    int buttonsPinValue = analogRead(buttonsPin);
    byte pressedButton = 0;
 
    if (buttonsPinValue > 823)
    {
        // button 6 has a value of about 878
        pressedButton = 6;
    }
    else if (buttonsPinValue > 725)
    {
        // button 5 has a value of about 768
        pressedButton = 5;
    }
    else if (buttonsPinValue > 649)
    {
        // button 4 has a value of about 683
        pressedButton = 4;
    }
    else if (buttonsPinValue > 586)
    {
        // button 3 has a value of about 614
        pressedButton = 3;
    }
    else if (buttonsPinValue > 535)
    {
        // button 2 has a value of about 559
        pressedButton = 2;
    }
    else if (buttonsPinValue > 492)
    {
        // button 1 has a value of about 512
        pressedButton = 1;
    }
    else if (buttonsPinValue > 450)
    {
        // if no button is pressed the value is of about 473
        pressedButton = 0;
    }
    else if (buttonsPinValue > 400)
    {
        // button 8 has a value of about 427
        pressedButton = 11;
    }
    else if (buttonsPinValue > 340)
    {
        // button 10 has a value of about 372
        pressedButton = 10;
    }
    else if (buttonsPinValue > 267)
    {
        // button 9 has a value of about 307
        pressedButton = 9;
    }
    else if (buttonsPinValue > 178)
    {
        // button 8 has a value of about 228
        pressedButton = 8;
    }
    else if (buttonsPinValue > 0)
    {
        // button 7 has a value of about 128
        pressedButton = 7;
    }
    return pressedButton;
}


Damit wäre das Projekt "Arduino MP3-Player" abgeschlossen. Die SD-Karte kann mit Tracks entsprechend der vorgesehenen Struktur befüllt werden, die dann über den Arduino abgespielt werden können. Allerdings kann das Befüllen und das Verwalten der Tracks noch über eine PC-Software vereinfacht werden. Näheres dazu aber das nächste Mal.



Weitere Blogeinträge

  1. Auswahl der Komponenten
  2. Das Entwicklungsbrett
  3. Das erste Einschalten
  4. Die Entwicklungsumgebung
  5. Knöpfe (digital)
  6. Mehrere Knöpfe (digital)
  7. Mehrere Knöpfe (analog)
  8. Potentiometer
  9. Das MP3 Shield
  10. Auswahl der Komponenten 2
  11. Auswahl der Komponenten (Zusammenfassung) 
  12. Punkt-Streifenrasterplatine und Knöpfe
  13. Punkt-Streifenrasterplatine und weitere Komponenten
  14. Das Gehäuse
  15. Sketch 1 (setup-Methode)
  16. Sketch 2 (loop-Methode)
  17. Sketch 3 (Der komplette Code)
  18. PC-Software


3 comments:

  1. Hallo chriga,

    vielen Dank für diese geniale Anleitung! Mein "Hörbjörn" ist das erste größere Arduino Projekt, ich habe mich bis hierhin durchgearbeitet und bis jetzt funktionierte auch alles wunderbar. Jedoch bekomme ich nun zwei Probleme:
    1. Die Wiedergabe mittels musicPlayer.startPlayingFile funktioniert nicht richtig. Die Wiedergabe von MP3s funktioniert grundsätzlich (getestet mit dem Adafruit Test Code). Allerdings nur solange ich das Ganze nicht über Interrupt laufen lasse. In deinem Code wird nichts wiedergegeben bis ich das Poti drehe, dann startet die Wiedergabe für einen Sekundenbruchteil und bricht wieder ab... Kennst Du das Problem oder hast eine Idee woran es liegen könnte?

    2. Irgendwann während der Wiedergabe (mal ganz am Anfang, mal nach einiger Zeit) bekomme ich ein ziemlich lautes statisches Rauschen auf den Lautsprecher. Hast Du eine Idee wo das herkommen kann oder was ich nochmals anschauen sollte?

    Ich würde mich riesig freuen wenn Du mir helfen könntest.

    Viele Grüße,
    Michael

    ReplyDelete
    Replies
    1. Hallo Michael,

      es freut mich, dass dir die Blog-Einträge zum Arduino MP3-Player gefallen.

      Leider bin ich mir nicht sicher, ob ich dir bei deinen Problemen weiterhelfen kann.

      1. Wenn der Poti auf ganz Leise gestellt ist, ist natürlich so gut wie nichts zu hören. Die Wiedergabe sollte aber unabhängig von der Poti-Stellung von statten gehen. Zu beachten ist, dass das Objekt mit Shield-Parametern und nicht mit Breakout-Parametern erzeugt wird (Adafruit_VS1053_FilePlayer(SHIELD_CS, SHIELD_DCS, DREQ, CARDCS)). Ansonsten dachte ich, dass die Funktion "startPlayingFile" nur mit Interrupt funktioniert (musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT)). Dass etwas nur kurz angespielt wird, kenne ich nur im Zusammenhang mit schwachen Batterien.

      2. Auch das Rauschen, das du beschreibst, kenne ich nur im Zusammenhang mit schwachen Batterien.

      Viele Grüße
      Chris

      Delete
  2. Hallo Chriga

    Besten Dank für die tolle Anleitung. Ich habe mit dem Code ein Problem. Wenn ich den kompletten Code kopiere und auf mein Board lade, dann kommt es nicht weiter als zu Zeile 126 bei "void loop()". Es rechnet konstant die millis hoch aber geht nicht weiter. In Visual habe ich die Bedingung: "millis()>¦¦ millis()>1" ist "True". Im Log habe ich folgenden Output: Port closed, Uploading I/O board, Opening port, port open (und leider nicht mehr).
    Kannst du mir diesbezüglich weiterhelfen?

    Herzliche Grüsse
    Christoph

    ReplyDelete