I'm looking to make a toy that plays a sound continuously while on and when you hold a button down, it plays a different audio sound.
I'm using the DFPlayer mini and an Arduino Nano although the diagram shows a regular Arduino, the pinouts are the same.
This is the code that I have so far and the wiring diagram I have:
#include "mp3tf16p.h"
// constants won't change.
MP3Player mp3(10,11);
const int buttonPin = 4; // the number of the pushbutton pin
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
// audio file names associated with the names on the SD card
#define idle 1
#define rev 2
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mp3.initialize();
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// Play Rev Sound:
mp3.playTrackNumber(rev,10);
;
} else {
// Play Idle Sound:
mp3.playTrackNumber(idle,30);
;
}
mp3.serialPrintStatus(MP3_ALL_MESSAGE);
Serial.println(buttonState);
}
The code works, sort of. The audio DOES play constantly, and does change when I hold the button down, but only after the first audio file has completed its full length.
The second audio file also has to fully complete, but I want it to change the second the button is held and released.
What am I missing?
By default, the mp3.playTrackNumber() function plays the entire track before anything else can happen, so even if you press the button, the current track needs to finish first.
What does this do if you try running it?
#include "mp3tf16p.h"
// constants won't change.
MP3Player mp3(10,11);
const int buttonPin = 4; // the number of the pushbutton pin
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
int lastButtonState = 0; // to remember the previous button state
// audio file names associated with the names on the SD card
#define idle 1
#define rev 2
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mp3.initialize();
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the button state has changed:
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
// Button is pressed, play Rev sound:
mp3.stop(); // stop the current track
mp3.playTrackNumber(rev, 10);
} else {
// Button is released, play Idle sound:
mp3.stop(); // stop the current track
mp3.playTrackNumber(idle, 30);
}
// Save the current button state for the next loop iteration
lastButtonState = buttonState;
}
// Print the current status and button state for debugging
mp3.serialPrintStatus(MP3_ALL_MESSAGE);
Serial.println(buttonState);
}
You didn't provide a link to the library you used so I couldn't check it, but the DFRobotDFPlayerMini library has an advertise method that I think might do what you're asking for.
I'm an absolute beginner here sorry, is library the same as the .h file? I used the library manager in the Arduino IDE to install the DFPlayer library?
But I did use a .h file that's here:
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#define MP3_ERROR_ONLY 1
#define MP3_ALL_MESSAGE 2
class MP3Player
{
private:
SoftwareSerial *mySoftwareSerial;
void statusOnSerial(uint8_t type, int value);
void waitPlayIsTerminated(void);
int p_RX;
int p_TX;
public:
DFRobotDFPlayerMini player;
MP3Player(int RX, int TX);
~MP3Player();
void playTrackNumber(int trackNumber, int volume, boolean waitPlayTerminated = true);
boolean playCompleted(void);
void initialize(void);
int serialPrintStatus(int errorOnly);
};
MP3Player::MP3Player(int RX, int TX)
{
p_TX = TX;
p_RX = RX;
}
MP3Player::~MP3Player()
{
}
void MP3Player::initialize(void)
{
mySoftwareSerial = new SoftwareSerial(p_RX, p_TX);
mySoftwareSerial->begin(9600);
Serial.println(F("Initializing MP3Player ..."));
if (!player.begin(*mySoftwareSerial,true,false))
{
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while (true)
;
}
player.volume(10);
Serial.println(F("MP3Player online."));
}
void MP3Player::playTrackNumber(int trackNumber, int volume, boolean waitPlayTerminated)
{
player.volume(volume);
player.play(trackNumber);
if (waitPlayTerminated)
{
waitPlayIsTerminated();
}
}
void MP3Player::waitPlayIsTerminated(void)
{
while (!playCompleted())
{
}
}
boolean MP3Player::playCompleted(void)
{
if (player.available())
{
return player.readType() == DFPlayerPlayFinished;
}
return false;
}
// Print the detail message from DFPlayer to handle different errors and states.
//
int MP3Player::serialPrintStatus(int verbose)
{
if (player.available())
{
uint8_t type = player.readType();
int value = player.read();
if (verbose == MP3_ERROR_ONLY)
{
if (type == DFPlayerError)
{
statusOnSerial(type, value);
}
}
else
{
statusOnSerial(type, value);
}
if(type == DFPlayerError) {
return value;
} else {
return 0;
}
}
}
void MP3Player::statusOnSerial(uint8_t type, int value)
{
switch (type)
{
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
case DFPlayerCardRemoved:
Serial.println(F("Card Removed!"));
break;
case DFPlayerCardOnline:
Serial.println(F("Card Online!"));
break;
case DFPlayerPlayFinished:
Serial.print(F("Number:"));
Serial.print(value);
Serial.println(F(" Play Finished!"));
break;
case DFPlayerError:
Serial.print(F("DFPlayerError:"));
switch (value)
{
case Busy:
Serial.println(F("Card not found"));
break;
case Sleeping:
Serial.println(F("Sleeping"));
break;
case SerialWrongStack:
Serial.println(F("Get Wrong Stack"));
break;
case CheckSumNotMatch:
Serial.println(F("Check Sum Not Match"));
break;
case FileIndexOut:
Serial.println(F("File Index Out of Bound"));
break;
case FileMismatch:
Serial.println(F("Cannot Find File"));
break;
case Advertise:
Serial.println(F("In Advertise"));
break;
default:
break;
}
break;
default:
break;
}
}
I don't know what that unnamed file is but if that's what you included it appears to be a different library that uses the DFRobotDFPlayerMini library. Which was completely hidden in your original file. I think I'll say good luck with your project and see myself out.
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
// Create software serial for communication with DFPlayer Mini
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
const int buttonPin = 4; // the number of the pushbutton pin
// Variables to track button state
int buttonState = 0;
int lastButtonState = 0; // to remember the previous button state
// Audio file numbers on the SD card
#define idle 1
#define rev 2
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) { // Use software serial to communicate with MP3
Serial.println("DFPlayer Mini not detected.");
while (true); // Halt execution if the DFPlayer isn't connected
}
myDFPlayer.volume(30); // Set volume level (0-30)
}
void loop() {
// Read the state of the pushbutton value
buttonState = digitalRead(buttonPin);
// Check if the button state has changed
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
// Button pressed, play the "rev" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.play(rev); // Play the rev sound
} else {
// Button released, play the "idle" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.play(idle); // Play the idle sound
}
// Save the current button state
lastButtonState = buttonState;
}
// Optional: Print button state to the serial monitor for debugging
Serial.println(buttonState);
}
This code doesn't include the DFplayer library. Go use that library, it will do what you want.
It's so close to perfect! I was worried because it wasn't looping the files, but I've just made the files longer and it does exactly what I need it to.
There is a tiny delay in switching between files, is this a limitation of the processing power of the nano?
The Nano also gets stuck playing the rev file if the button is simply pressed for a fraction of a second and the Nano has to be reset to stop it, any ideas what's causing that?
Thank you SO MUCH! for your help, I'm genuinely ecstatic with what you've done for me, I'm having so much fun!
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
// Create software serial for communication with DFPlayer Mini
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
const int buttonPin = 4; // the number of the pushbutton pin
// Variables to track button state
int buttonState = 0;
int lastButtonState = 0; // to remember the previous button state
// Audio file numbers on the SD card
#define idle 1
#define rev 2
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) { // Use software serial to communicate with MP3
Serial.println("DFPlayer Mini not detected.");
while (true); // Halt execution if the DFPlayer isn't connected
}
myDFPlayer.volume(30); // Set volume level (0-30)
}
void loop() {
// Read the state of the pushbutton value
buttonState = digitalRead(buttonPin);
// Check if the button state has changed
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
// Button pressed, play and loop the "rev" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(rev); // Play and loop the rev sound
} else {
// Button released, play and loop the "idle" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(idle); // Play and loop the idle sound
}
// Save the current button state
lastButtonState = buttonState;
}
// Optional: Print button state to the serial monitor for debugging
Serial.println(buttonState);
}
More likely the DFPlayer Mini. The Nano should be fast enough, but the DFPlayer has to close the file safely, switch files, open the new file, process it, and play it. I feel like there isn't much you can do about that.
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
// Create software serial for communication with DFPlayer Mini
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
const int buttonPin = 4; // the number of the pushbutton pin
// Variables to track button state
int buttonState = 0;
int lastButtonState = 0; // to remember the previous button state
unsigned long lastDebounceTime = 0; // the last time the button state was toggled
unsigned long debounceDelay = 50; // debounce delay in milliseconds
// Audio file numbers on the SD card
#define idle 1
#define rev 2
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) { // Use software serial to communicate with MP3
Serial.println("DFPlayer Mini not detected.");
while (true); // Halt execution if the DFPlayer isn't connected
}
myDFPlayer.volume(30); // Set volume level (0-30)
}
void loop() {
// Read the state of the pushbutton value
int reading = digitalRead(buttonPin);
// Debounce the button press
if (reading != lastButtonState) {
lastDebounceTime = millis(); // Reset the debounce timer
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// If the button state has changed and it's stable
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
// Button pressed, play and loop the "rev" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(rev); // Play and loop the rev sound
} else {
// Button released, play and loop the "idle" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(idle); // Play and loop the idle sound
}
}
}
// Save the last button state
lastButtonState = reading;
// Optional: Print button state to the serial monitor for debugging
Serial.println(buttonState);
}
Sadly no joy with the debounce, I'm using a simple tactile button if that changes anything?
I'm curious, the Nano can play simple audio files, and seeing as I'm only using 2 files that are small. Could I completely omit the DRFplayer mini and simply power the speaker through a simple audio amp?
OR do you feel the DRF is my best option?
The DFPlayer isn't super fast. If you send too many instructions too quickly, it will ignore the second instruction.
Maybe this will fix it??
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
// Create software serial for communication with DFPlayer Mini
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
const int buttonPin = 4; // the number of the pushbutton pin
// Variables to track button state
int buttonState = 0; // Current state of the button
int lastButtonState = 0; // Previous state of the button
unsigned long lastDebounceTime = 0; // Last time the button state was toggled
unsigned long debounceDelay = 50; // Debounce delay in milliseconds
// Audio file numbers on the SD card
#define idle 1
#define rev 2
// Button state flag
bool isRevPlaying = false; // Flag to indicate if "rev" sound is playing
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) { // Use software serial to communicate with MP3
Serial.println("DFPlayer Mini not detected.");
while (true); // Halt execution if the DFPlayer isn't connected
}
myDFPlayer.volume(30); // Set volume level (0-30)
myDFPlayer.loop(idle); // Start playing and looping the idle sound initially
}
void loop() {
// Read the state of the pushbutton value
int reading = digitalRead(buttonPin);
// Debounce the button press
if (reading != lastButtonState) {
lastDebounceTime = millis(); // Reset the debounce timer
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// If the button state has changed and it's stable
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH && !isRevPlaying) {
// Button pressed, play and loop the "rev" sound
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(rev); // Play and loop the rev sound
isRevPlaying = true; // Set the flag to indicate "rev" is playing
} else if (buttonState == LOW && isRevPlaying) {
// Button released, ensure the idle track is playing
myDFPlayer.pause(); // Pause the current track
myDFPlayer.loop(idle); // Play and loop the idle sound
isRevPlaying = false; // Clear the flag to indicate "rev" is not playing
}
}
}
// Save the last button state
lastButtonState = reading;
// Optional: Print button state to the serial monitor for debugging
Serial.println(buttonState);
}
But I would still expect that when you release the button after a quick press, the rev track will continue to play till the DFPlayer has finished the background tasks it is still doing, though should be very quick.
Sadly it still gets caught up with the button press, but honestly, it's at a stage where the project can progress and fine tweaking can go to the back burner, I will continue to learn and try to find a solution.
Thank you for everything, if you have any other ideas, I'm open to trying