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_WRAMADDR, para_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_WRAMADDR, para_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
- Auswahl der Komponenten
- Das Entwicklungsbrett
- Das erste Einschalten
- Die Entwicklungsumgebung
- Knöpfe (digital)
- Mehrere Knöpfe (digital)
- Mehrere Knöpfe (analog)
- Potentiometer
- Das MP3 Shield
- Auswahl der Komponenten 2
- Auswahl der Komponenten (Zusammenfassung)
- Punkt-Streifenrasterplatine und Knöpfe
- Punkt-Streifenrasterplatine und weitere Komponenten
- Das Gehäuse
- Sketch 1 (setup-Methode)
- Sketch 2 (loop-Methode)
- Sketch 3 (Der komplette Code)
- PC-Software
Hallo Chrigas,
ReplyDeleteDanke 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
Hallo Dirk,
Deletevielen 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