Tuesday, March 31, 2015

Audino - Arduino MP3-Player (16) - Sketch 2 (loop-Methode)

Nachdem wir uns das letzte Mal die setup() Methode angeschaut haben, schauen wir uns jetzt die loop() Methode an.

In der loop() Methode wird zuerst überprüft, ob gerade ein Track abgespielt wird. Ist das nicht der Fall, wird der nächste Track abgespielt.

Beim Einschalten des Geräts wird auch kein Track abgespielt. Da der zuletzt abgespielte Track wiedergegeben werden soll (und nicht der Nächste), wurde in der setup() Methode der Index auf den Vorgänger gesetzt.

// play next song if player stopped
if (musicPlayer.stopped())
{
    playNext();
}

In der playNext() Methode wird der Index für den aktuellen Track um eins hochgezählt. Ist der Index größer als die Anzahl der Tracks im aktuellen Ordner, wird der Index auf den ersten Track gesetzt. Anschließend wird die playCurrent() Methode aufgerufen.

void playNext()
{
    currentFile++;
    if (currentFile > numberOfFiles[currentFolder])
    {
        currentFile = 1;
    }
    playCurrent();
}

In der playCurrent() Methode wird überprüft, ob der aktuell ausgewählte Ordner überhaupt Tracks beinhaltet - nur dann wird fortgefahren. Anschließend wird abgespeichert welcher nun der aktuelle Track ist (rememberCurrentTrack() Methode), ehe der aktuelle Track-Name aus currentFolder und currentFile zusammengesetzt und abgespielt wird.

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

In der rememberCurrentTrack() Methode wird in einer Datei auf der SD-Karte currentFolder und currentFile abgespeichert. Dabei wird die Datei zuvor gelöscht, falls die Datei bereits existiert.

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();
}

Als nächstes wird in der loop() Methode überprüft, ob die Lautstärke geändert werden soll. Dazu wird die checkVolume() Methode aufgerufen, in der als erstes der aktuelle Wert des Potentiometers abgerufen wird. Der ausgelesene Wert wird auf den Lautstärkebereich übertragen. Falls eine Änderung um 2 stattgefunden hat, wird der neue Wert gesetzt.

// the pin of the potentiometer that is used to control the volume
const int volumePin = A1;

// the current volume level, set to min at start
byte volumeState = 254;

// 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 (limit max volume to 20) to min=254 
    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);
    }
}

Schließlich wird in der loop() Methode überprüft, ob ein Knopf gedrückt worden ist und gegebenenfalls eine entsprechende Aktion ausgeführt. Dazu wird die checkButtons() Methode aufgerufen. In dieser wird als erstes über getPressedButton() ermittelt, ob ein Knopf gedrückt worden ist und wenn ja welcher.

Ist ein Knopf gedrückt worden, wird überprüft ob der Knopf einem Track / einer Play List entspricht oder einem Funktionsknopf. Dabei wird nur eine Aktion ausgeführt, wenn zuvor kein Knopf (released=true) gedrückt worden war. Anschließend wird released=false gesetzt.

Handelt es sich um einen Track/Play List Knopf, wird der aktuelle Track angehalten. War der zuvor abgespielte Track schon in dem Ordner, dem der gedrückte Knopf entspricht, wird der nächste Track abgespielt. War der zuvor abgespielte Track nicht in dem Ordner, dem der gedrückte Knopf entspricht, wird der aktuelle Ordner gesetzt und der erste Track abgespielt.

Handelt es sich um einen Funktions-Knopf, wird überprüft um welchen es sich handelt. Beim Zurück-Knopf wird der aktuelle Track angehalten. Schließlich wird überprüft, ob der Zurück-Knopf wiederholt innerhalb einer Sekunde gedrückt worden ist oder ob der Zurück-Knopf das erste Mal gedrückt worden ist. Ist der Zurück-Knopf das erste Mal gedrückt worden, wird der zuletzt gespielte Track von Vorne abgespielt. Ist der Zurück-Knopf wiederholt innerhalb einer Sekunde gedrückt worden, wird die Methode playPrevious() aufgerufen. Beim Suchvorlauf-Knopf wird die Geschwindigkeit mit der der Track abgespielt wird erhöht.

Ist kein Knopf gedrückt worden, wird released=true gesetzt.Wurde zuletzt der Suchvorlauf-Knopf verwendet, wird die Abspielgewchwindigkeit wieder zurückgesetzt.

// wait before next click is recognized
const int buttonPressedDelay = 3000;
// 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;

// VS1053 play speed parameter
#define para_playSpeed 0x1E04
 

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

Der gedrückte Knopf wird über den Code aus Mehrere Knöpfe (analog) ermittelt.

// the number of the pin that is used for the pushbuttons
const int buttonsPin = A0;

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

In der playPrevious() Methode wird der Index für den aktuellen Track um eins runtergezählt. Ist der Index kleiner als 1, wird der Index auf den letzten Track gesetzt. Anschließend wird die playCurrent() Methode aufgerufen.

void playPrevious()
{
    currentFile--;
    if (currentFile < 1)
    {
        currentFile = numberOfFiles[currentFolder];
    }
    playCurrent();
}


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


2 comments:

  1. Hallo Chrigas,

    Danke für die super Dokumentation, ich habe gerade meine erste "Hörmine" zum Laufen bekommen! Einige kleine Codeanpassungen werde ich noch vornehmen, z.B. verbesserte Suche der Tracks auf der SD-Karte und Statussignalisierung bei fehlender Karte. Wo kann ich Dir die Anpassungen zusenden?
    Ansonsten ist die Software sehr sauber und beschränkt sich auf das was sie tun soll - ohne Schnickschnack.
    Das ist gut so für den Kinder-Player!

    Schöne Grüße
    Dirk

    ReplyDelete
    Replies
    1. Hallo Dirk,

      vielen Dank für deinen Beitrag! Freut mich, dass dir die Dokumentation gefällt. Ich bin auf deine Codeanpassungen schon ganz gespannt. Du kannst mir diese an chriga (punkt) blog (at) gmail (punkt) com zusenden.

      Viele Grüße

      Delete