Taking action on button presses

Hi, some advice please, I’m using a DFPlayer mini with an Arduino nano. A single button press plays one of four tracks, depending on which of the tracks number is selected. I need each press of the button to play a different track on each press, only when the track playing finishes. I thought of using the button count function, so for each press of the button corresponds to the next track.
Would this be the best method, using count, or is there a better simpler way?

That would work, just que up the tracks and when the last finished shut down.

Consider: using the DFPlayerMini I/O Mode, where a short-press on IO_1 plays next file, and IO_2 plays previous file, use an array of button pins, for example, byte pins[]={2, 3, 4, 5]; and when a button is pressed, the program knows how many "short presses" to give the DFPlayerMini to skip to the desired file.

Thanks for your prompt reply, The DFPlayer library has a “play next” facility, but couldn’t get it to work! Cheers Jevray

Thanks for your response, I’m using serial comes, tx,rx. The button press is in fact a pir movement detector, giving a low output to trigger the DFPlayer. For each trigger, one of four recorded messages can be heard. For simplicity and programming I use a button while the project is under going bench tests.

I have included my sketch, which works fine, and also provides a random file playing mode (not as intended) but works great this way. However, I'm using the Busy Pin on the DFPlayer to prevent another file from playing before the current one is still playing, but not sure if it's in the correct position at the beginning of the main loop.
Would appreciate it if somebody would kindly check over my code please, as I'm a mere novice at programming, please feel free to make alterations. Kind regards

// Boris 01/09/2024
// Using random mp3 files

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#define RX_PIN 10        // Connect DFPlayer Mini TX pin to Arduino D10
#define TX_PIN 11        // Connect DFPlayer Mini RX pin to Arduino D11
#define sensor_f_PIN 12  // Connect  front sensor to Arduino D12
int count = 0;
int ledPin = 13;
int busyPin = 9;   
SoftwareSerial mySerial(RX_PIN, TX_PIN);  // RX, TX
DFRobotDFPlayerMini myDFPlayer;

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  if (!myDFPlayer.begin(mySerial))
    myDFPlayer.volume(10);  // Set volume (0 to 30)
  pinMode(sensor_f_PIN, INPUT_PULLUP);
  pinMode(busyPin,INPUT);
}
void loop() {
  if (busyPin = (LOW))
  return;
  if (count = (1))
    ;
  
  if (digitalRead(sensor_f_PIN) == LOW) {
    delay(60);
    myDFPlayer.play(1);
    Serial.println(F("track 1:"));
    delay(1000);  // wait for a second
    count++;
  } else {

    count++;  // add one (1) to our count
    if (count = (2))
      ;
    if (digitalRead(sensor_f_PIN) == LOW) {
      delay(60);
      myDFPlayer.play(2);
      Serial.println(F("track 2:"));
      delay(1000);  // wait for a second
      count++;
    } else {
      if (count = (3))
        ;  //if (count < maxnum)
      if (digitalRead(sensor_f_PIN) == LOW) {
        delay(60);
        Serial.println(F("track 3:"));
        myDFPlayer.play(3);
        delay(1000);  // wait for a second
        count++;
      } else {
        if (count = (4))
          ;  //if (count < maxnum)
        if (digitalRead(sensor_f_PIN) == LOW) {
          delay(60);
          myDFPlayer.play(4);
          Serial.println(F("track 4:"));
          delay(1000);  // wait for a second
          }
          count++;
        }
      }
    }
  }
]

In this line you are setting busyPin to LOW. I think you meant to compare the state of busyPin to LOW, like this...

if (busyPin == LOW)...

My next question: does "return" stop the program from executing, or does the program go back to the main() wrapper, then execute setup() and loop()? To check this, put a print statement in setup() showing that you are in setup()... If this is happening, try another method, like this (assuming your busyPin is tied low):

while (!busyPin); // wait for busyPin to go HIGH

Thanks for pointing out (busy pin =) I’ll correct it. Once play is executed, it must not be interrupted, until the busy pin returns to a high state.. Thinking about it, if play is activated within the loop, it will still restart the player. So the players busy pin must halt the loop to prevent the play pin from influencing the selected track while playing. I could probably achieve this by an additional circuit, much easier if we can tweak the code. Jevray

You’re saying your sketch works fine.
If so, even if your code might not be great ( I haven’t studied it), presumably job done, yes?

@Terrypin Not had a chance yet to do another test, will post back my results. Thanks for your reply. Jevray

Saying that you know how long the tracks are -- you could 'simply', based upon millis(), not check for further actuations till the track time runs out.

Good point, I could do it that way and may end up doing it. However I came across this statement in the DF library:- if (digitalRead(pinDfpBusy) == HIGH) which relates to this line in Set Up:- pinMode(pinDfpBusy, INPUT); maybe I can use this while the busy pin is low (playing) it prevents the play pin interfering. i.e. executing a return; while playing. Going to give it a try. Thanks again for coming back to me. Jevray

All the statements like these

        if (count = (4))
          ;  //if (count < maxnum)

are correct complete statements that do… nothing. So I don't know what that is meant to accomplish.

As for

, when the track is commanded to start, the busy pin will soon show that the player is playing the track, it will remain saying the track is playing until it is over.

Soon. Not instantly you send the command, as the command takes a l o n g t i m e to get to the player.

On another thread, this logic solved that unfortunate and impossible to fix fact:

// Play DFR audio and check for playback completion

void playAudio(int fileNumber) {
  myDFPlayer.playMp3Folder(fileNumber);

  while (digitalRead(BUSY_PIN) != BUSY) {
    ;  // This loop waits for the DFR player to get BUSY.
  }

  while (digitalRead(BUSY_PIN) == BUSY) {
    ;  // This loop executes while the DFR BUSY pin is LOW indicating playback in progress.
  }
}

Put your finger on that - the first while statement waits for the command to filter in and get the track started, the second while statement hangs while the player remains busy.

You can work with the BUSY_PIN however you like. The above function blocks while the track is playing, but would not have to do.

You just have to know the signal doesn't appear right away. Then use it however.

This is the thread, if you need to see the slog.

HTH

a7

This simple modification makes it less blocky. It only does the first while statement.

// Play DFR audio and check for playback completion

void playAudio(int fileNumber) {
  myDFPlayer.playMp3Folder(fileNumber);

  while (digitalRead(BUSY_PIN) != BUSY) {
    ;  // This loop waits for the DFR player to get BUSY.
  }
}

It will return after the track is actually be playing. In the code that called it, the BUSY_PIN will be valid and useful.

a7

@alto777, Thanks for your reply and suggestions, I’ll include those snippets into my sketch. Just to recap ,for each button press, one of four individual tracks will play, after which, when all four tracks have played, the sequence repeats itself. If a button press occurs while playing, it must not interfere with the track that’s in play. My code worked ok ish, but the tracks played in a random order! that anomaly is a bonus for me. But whenever a button press was executed, during play, it looped back to the beginning of that same track while playing. I have now encountered a problem with the loop hanging after playing the first track following a button press, so something is clearly at fault. My sketch was put together from sketch samples of DFPlayer’s using a play button. Still a novice, but your guidance here is invaluable. Thanks Jevray

You're supposed to post your current sketch - takes the guesswork out of it.

My sketch was posted #6 I haven’t yet updated since then, but following suggestions, I intend to update my sketch, going forward.
Will post updates when tested out. Regards Jevray

This seems simple. The following is pseudocode, an informal but close to code expression of an algorithm:

// a global variable to track the track

int theTrack = 1;		// starting track

// in the loop, all you gotta do is
loop is
  if player is not playing 
    if the button is pressed
      start track theTrack + wait for the player to become busy
      add 1 to theTtrack for next time (reset to 1 if you just started track 4)
    
  else (player is playing)
    <nothing to do! we are waiting for the player to finich the track>

The rest of the loop is not blocked except the brief time it takes for the player to kick in and look busy so you could use nearly 100 percent of the processor resources to do anything else, like manage a game of tic-tac-toe or whatever

I left the "button is pressed", in practice this means you have to be off the button before the current track finishes, or the next track will be launched. There are ways to handle this, we can get to those. For now, we can exploit the fact that the button will simply not be looked at while the player is playing, giving someone time to get her fat finger off the button.

Or you could call it a feature - keep the button down and the tracks repeat over and over while it is. :expressionless:

Real button handling would look at the button to spot the transition the button makes, so the next track is launched when the button becomes pressed rather than because it is (still) pressed. Sooner later you will have to understand that; let's get it working with the known flaw (or feature!) first.

a7

To be 100% clear, assume the sketch has been freshly uploaded or reset. On each press do you then want play to be 1, 2, 3, 4, 1, 2, 3… etc? Or, as implied in post 6, do you now want at least the first play to be randomly chosen? Say it chose 3: do you want subsequent plays to be 4, 1, 2, 3, 4, 1, 2, etc? Or all random again? If so, what should happen about repetition? And should anything special happen after four presses?

Of course, these can wait until you have the key stuff working with @alto777's approach. But meanwhile perhaps it might be helpful to say more about the intended application?

… we can talk about an improved function I came up with.

The non-blocking function that launches a track by number has a flaw in that it can get caught (hang up, fail to return) if the player never gets busy for some reason.

I do not use the DFPlayer, so this will have to be speculative.

Starting with the function from earlier

// Play DFR audio and check for playback completion

void playAudio(int fileNumber) {
  myDFPlayer.playMp3Folder(fileNumber);

  while (digitalRead(BUSY_PIN) != BUSY) {
    ;  // This loop waits for the DFR player to get BUSY.
  }
}

we see it returns as soon as the busy signal has appeared. It would never return if that signal failed to appear.

The function instead could return 0 to indicate that the track is playing and also that the BUSY_PIN will be useful for monitoring the player.

Timer logic should be added so that if too much time has passed, the function returns a non-zero value. The busy signal failed to appear, the track is no playing.

It occurred to me that the call to the playMp3Folder method might return something itself, which could be used to also return a non-zero value. The track is not playing.

I was surprised to find that playMp3Folder is a function returning void, which means one cannot know other than through the busy pin whether the command worked or not:

void playMP3Folder(uint16_t track);

so just the timer then.

// Play DFR audio by file number and check for success

int playAudio(int fileNumber) {
  int returnCode = 1;  // we hope not
  myDFPlayer.playMp3Folder(fileNumber);

  unsigned long timer = millis();
  while (millis() - timer < 333) {
    if (digitalRead(BUSY_PIN) == BUSY) {
      returnCode = 0;
      break;
    }
  }

  return returnCode;   // 0 = playing, 1 is some kinda error
}

I don't know how long that should be. If the time delay is because of serial communications, 333 milliseconds is extremely forgiving. There may be another cause; some testing or experience with the player would allow use of a smaller number. The function would,d be faster to say it had failed,

a7