Continued problems with DFPlayer readFileCountsInFolder method

Hello forum,

I’ve used DFPlayers for years, but I’ve never had a need to use the readFileCountsInFolder method. Well, the time has come that I need it and it’s not working. I get a -1 return from the first call and get no useful info back from the printDetail function (outlined in the library example). For subsequent folder reads I get what seem to be valid file counts, but not for the folder for which I’m requesting the count!

I saw in this now-closed topic that this issue is discussed:

DFPlayer Folder count - Other Hardware / Audio - Arduino Forum

But I’m not using a clone - I’m using an official DFRobot player and seeing the same issue. The workaround described in the above post of calling the method THREE times and only using the last return value DOES SEEM TO WORK, but I hate it! How can I be sure it will always work? I started with just two calls and that seemed to be working, but when I moved these calls within my code they stopped working and I had to go to three calls. Plus, these calls take a REALLY LONG TIME to run. Getting the counts for just a few folders using this approach takes seconds of run time.

I did see that there are other libraries out there, but I’ve never used them and would hate to have to re-write all my code to work with a different API. Besides, I saw that in at least one of these libraries (I forget which one) it is noted that this issue seems to be associated with certain clone chipsets and suggests some other workarounds (like first playing a file from the folder before reading the count) that are necessary, so I’m not sure a different library will help. I’ve tried these workarounds and the only one that seems to work is calling the function three times.

I’ve tried reaching out to DFRobot, but I’ve not had any response. Anyone have any ideas? This is such a great little module, and all the rest of my code (and previous implementations) are working flawlessly.

Thanks for any help!

I'd probably start by putting one of those little $10 USB logic analyzers on the Rx & Tx lines just to see what's actually going on. You could see both the transmitted message and whatever's returned. Once you can see the actual messages you'd have a starting point on "what's good/what needs to be looked at".

If you run the full function.ino file on GitHub repository does the function work?
I have three genuine mini dfPlayers and all of them are slightly different they also arent clones.
They do work with all functions I remember testing them thoroughly.
I remember something about how the SD card is formatted is super important when dealing with multiple files. Hope this helps

What does your root folder structure look like? Are the music file in the root or in an actual folder?

I tested with the FullFunction example and everything works great - except all the read() functions. I don’t think any are returning the right values. The first call returns -1 and all the rest are numbers I can’t interpret - but they are the same every time I run.

I’m using a new SanDisk SD card that came pre-formatted. I have six folders under the root folder - 00, 01, 02, 03, 04, and 05. All the files in the folders start with a unique, sequential, three-digit number. In past implementations I just had all the audio files under an MP3 directory under the root and audio files with four-digit starting numbers, but this time I need to segregate the folders. I don’t think I’ve ever built a project that needed to use any of the read calls. Tomorrow I’ll try reformatting the SD Card, but since all the initialize, setup, and play functions seem to be working this seems like a stretch. I’ll also build up a separate stand-alone hardware testbed and try that too.

Stupid question but do you have a straight wire going from the DFPlayer TX pin to the Arduino or is there a resistor on it too?

FOUND IT! Took the better part of a morning but finally figured it out. Jump to the end if you just want to see the fix.

When I looked more closely at the reads that didn’t align after the first call that returned the -1, I noticed that they were off by one folder. So, clearly the data was getting back, but it was out of sync. I messed around putting a bunch of delays in, playing with the setTimeOut function, making sure the serial port was flushed, but nothing made any difference. Next, I dug into the DFPlayer code and found there is a debug flag (nice!). With debug enabled I could see that the read routines were indeed reporting back before the read info had been provided from the DFPlayer, but I also saw that there was some sort of acknowledgement from the DFPlayer for every DFPlayer library call - even the write calls. The read calls generate two responses, one with the requested data and then another for the ack. What is happening is that every call to the DFPlayer generates an ack that is interpreted by a subsequent read call as the requested data. This continues with every call because there is always one unread ack call hanging around. Clearing the serial buffer after a call with available() does work, but ONLY if you also put in a small delay to give time for the real response, since the library interprets the ack as the response and returns immediately.

Digging into the code more, I saw there is an ack flag setting, and that this flag can be set or disabled in the DFPlayer.begin call (it’s one of the optional parameters). In the example files this flag is explicitly set to true, and if it’s not supplied it also defaults to true. BUT when explicitly set it to false in my DFPlayer.begin call, then the acks went away and everything worked. Sooooo, it looks like the library is set up to enable or disable the ack feature, but that it does not appropriately deal with acks when they are enabled.

I tested this on both my official DFRobot player as well as some clones and it was consistent - setting the ack=true in the DFPlayer.begin call worked and not setting it caused any reads to fail.

Thanks much to all who replied to my original post - you gave me some debugging ideas that helped put me on the path.

Rats - well that definitely fixed one problem, but there is still at least one other issue. If I put a play a file and then do a read it breaks again. For some reason the play is generating two “Play Finished” messages (not sure why two, but even one would be a problem). So, these need to be flushed or we’re back in a similar situation where these receive messages are being queued up and interpreted by the read function. More to do tomorrow.

I’m really not sure how the read calls ever worked if they were called after a play call.

So, in trying to understand the play issue outlined in my last post, I went back to the ack issue and dug into the library code. When the ack is enabled, every DFPlayer library call first waits (with a timeout) for the last ack to be received before continuing on and sending the new command to the DFPlayer. This works fine for commands that are not read commands. For read commands the library still waits for the last ack and then waits for the data response from the DFPlayer. The problem is that the ack response is coming in before the data response. So, the ack response is being interpreted as the data response and then the actual data response gets cleared off in the next send because it is treated as an ack respose. It must be the case that older DFPlayers had the order reversed and the data response came in before the ack response. So, turning off the acks resolves this problem. Since some older posts note that this is only an issue for clones, I wonder if DFRobot has updated their chipset on their newer modules to the same chipsets used in the clones. If anyone has a really old DFRobot DFPlayer then it would be great to verify this. All you need to do is to uncomment the _DEBUG define in DFRobotDFPlayerMini.h and look to see if the ack messages are returned before the data messages when invoking a read method. The ack messages are “:7E FF 6 41 0 0 0 FE BA EF” where the 41 designates this as an acknowledgement.

However, even when the acks are disabled, if we send a play command the DFPlayer eventually responds with a “play finished” message (actually two of them for some reason). The next time we send a read command to the DFPlayer the play finished message is hanging out in the receive buffer and is interpreted as the data response to the read command. I don’t see anything in the library software that is designed to deal with this situation. So, I’m not clear on how this ever worked. If anyone has any insights here, I’d love to hear them!

Now, it is the case that when calling a read command most of these methods will do a sanity check on the data return and send back a “-1” if it doesn’t match what is expected. (For some inexplicable reason the readVolume call does not do this check, so it interprets the play finished message as valid and returns bad data.) I was thinking that I could just check the return value from the readFileCountsInFolder() call and if it’s a “-1” then call it again until I get back a good value. Well, this works for the first read call. The problem now is that we’ve sent multiple requests to the DFPlayer and these responses start buffering up. So, the next call to a read method will send the new command, wait for the response, and pull one of the previous read responses from the serial buffer. Now the response checks out as a valid data response, but the data is actually from the last read call.

Since the play finished messages can come in at any time, there is no way to anticipate when these could happen unless we always wait for play to finish. Any read calls made after a play but before the play finished is received would still be ok, but any read calls after the play finished messages will not work. If we wait for the play to finish, then we can delay a bit to make sure the play finished messages are queued and flush the receive buffer with calls to available() before calling read methods. But it’s hard for me to believe that this is how the library was intended to operate. The library could be modified to ignore play response messages, but if anyone is actually using these play finished messages to determine when play is completed (rather than the hardware busy pin) then these messages would be lost if a read call was made between the time the play completed and the user polled for the play response message.

So, short of modifying the library, I don’t see a way around this other than to just make sure that no read methods are invoked after play methods. My guess is that this is usually the way most user code is set up: get the DFPlayer configuration info during initialization and then start invoking play methods. Still, seems it should be fixed along with the ack issue. I’m really curious if anyone can validate what I’m seeing. If so, then perhaps we can all send messages to DFRobot asking them to fix the library! It looks like it hasn’t been updated in years.

Sorry for the long, complex post, but as I have seen that others have had these same problems and there has not been any good explanation, I wanted to get it all documented.

Ok, for those of you still following along (guessing not many) and who don’t think I’ve lost my mind (guessing even fewer, if any), one more little twist to this story that I think explains why the playback issue is pretty rare. It turns out this is really only an issue when you wait for playback to compete and then call a read method. If a read method is called before the play is complete then the playback is canceled and no “play finished” return messages are sent by the DFPlayer.

So, for my implementation I was trying to have a file play as fast possible on power-up. I have six folders on the SD Card, but I really only need to know how many files are in folder 00 during setup - I can wait until later to get the rest of the folder counts. So, what I am doing in setup is initializing the DFPlayer, reading the folder 00 file count, playing one of these files, waiting for play to complete using the busy pin, and then getting the rest of the folder counts. Because I’m waiting for the file to complete, I’m getting the Play Finished messages in the read buffer and this is screwing up the rest of the folder counts. If I don’t wait then the folder counts are correct, but the calls to get these counts will halt the playback, which I don’t want to do. So, my options are to wait until the playback is complete, delay a bit, and flush the Play Finished messages with an available() call, or wait until way later in the program get the other folder counts when I’m sure the play is complete. I guess I could also not use the busy pin and instead wait for the Play Complete messages, but I thought the busy pin would be a better approach (ha!).

Ok, I get that this is likely a rare implementation situation but still seems like it should be fixed or at least documented. Regardless, the ack problem really does need to be addressed!

Ok, so I’ve had my fill and I’m guessing I’ve lost my audience. But I’m also guessing that someone else will eventually run into these problems, so hopefully they will appreciate this. Below is code that works as I need it to - initialize the DFPlayer, set the volume, get the number of files in folder 1 of the SD Card, play a random file from that folder, wait until play is finished, and then read the number of files on the remaining folders. This code works.

Note, I actually have quite a few more folders, and these calls are REALLY slow. I want to play a file as quickly as possible on startup. This is why I’ve fought my way through this.

But the saga has one more twist… Just for grins, after I implemented this on my cheap clone DFPlayer, I decided I should also test it on a recently acquired official DFRobot DFPlayer - and there were issues. First, I originally started my folders at 00. This worked on the clone, but not on the DFRobot player. In their defense, buried in their example code there is a comment that says folders are numbered starting with 1. However, this was not the only problem. Astute readers will note that in the begin() call, not only did I have to change the ack parameter to false (see above saga for the horrible details), but I also had to set the reset parameter to false. When this flag is true, the DFRobot player (and only the DFRobot player) does something very odd - my first call to readFileCountsInFolder() returns 0. In fact, ANY read call will return zero. And this is not an issue with the library - the chipset is returning 0 for this query. It turns out that any read call will return zero. In fact, if I get rid of the volume call then the first two read calls will return zero. If I make any two calls before a read call then everything is fine. No amount of delay() or value sent to setTimeOut() seems to help. So, yeah, probably not a situation that comes up very often. Note that the examples make many set calls before any read calls are made, so this wouldn’t show up.

#include <printf.h>
#include <SoftwareSerial.h> 
#include <DFRobotDFPlayerMini.h>

SoftwareSerial mySoftwareSerial(8, 7);
DFRobotDFPlayerMini myDFPlayer;

void setup() {
  int folder1Cnt, folder2Cnt, folder3Cnt;

  Serial.begin(115200);
  while (!Serial);    // Wait for serial to be ready
  printf_begin();

  printf_P(PSTR("DFPlayer ... "));
  mySoftwareSerial.begin(9600);
  if (!myDFPlayer.begin(mySoftwareSerial, /*isACK = */false, /*doReset = */false)) {
    printf_P(PSTR("Failed to initialize\n"));
    while (1);
  }
  printf_P(PSTR("Initialized\n"));
  myDFPlayer.volume(15);

  folder1Cnt = myDFPlayer.readFileCountsInFolder(1);    // Get the file count from folder 1
  myDFPlayer.playFolder(1, random(folder1Cnt));         // Play a random file from folder 1
  // Yes, I'm aware that I didn't seed the random number generator - removed this for brevity

  // If we don't wait for the playback to complete, it will be interrupted by the read calls below.
  // So wait for the "play complete" messages - note there are two of these for each play. 
  // Even if we use the hardware Busy pin, we have to make these calls or the "play complete"
  // messages will be interpreted by the library as responses to the later read calls.  Note
  // we also have to call either read() or readType() function to clear the library available flag.
  while(!myDFPlayer.available());
  myDFPlayer.read();
  while(!myDFPlayer.available());
  myDFPlayer.read();

  folder2Cnt = myDFPlayer.readFileCountsInFolder(2);
  folder3Cnt = myDFPlayer.readFileCountsInFolder(3);

  printf_P(PSTR("Folder Counts: 1)%d 2)%d 3)%d\n"), folder1Cnt, folder2Cnt, folder3Cnt);
}

void loop() { }

Not me anyway. If I wasn’t trying desperately to finish a DFR Player project myself for a Christmas gift I’d be trying to reproduce some of your suggestions. And yes I have had similar issues and no consistent solution. The module can be extremely fussy about delays both before and after commands. One factor that usually crops up if you’re using the busy pin is the variable delay needed after using it before it can be relied upon. Viewing it on my handheld ‘scope sometimes offers straws I can clutch at. Overall, trial and error remains my only reliable tool.

Best of luck.

Thanks Terrypin - sorry for your troubles, but glad for some comradery.

Yeah, below is how I’ve been using the busy pin. It’s been working fine for me - until I ran into the problem mentioned above about having to clear out the Play Finished messages when using read functions after a play.

cmdStartTime is a global variable that I can subtract from the current time to get the real time delta from the start of play. I need this to synchronize events. I was going crazy for a while because I would adjust all my timing and get it perfect and then on a later run I’d find the timings had changed. I didn’t realize that there was so much variability in the time it takes to actually start playing!

I should probably add some timeouts on the busy pin checks. I planned to do that but kinda forgot about it because I’ve never had a lock up.

  enum playwait {NO_WAIT, WAIT_UNTIL_STARTS, WAIT_UNTIL_DONE};
// The play function will initiate playing a file from a 
// given folder on the DFPlayer.  There are three options for
// waiting:
// 1) WAIT_UNTIL_STARTS - waits for the DFPlayer BUSY pin to
// go high indicating it has started playing and store the
// start time. This is useful because the amount of time it 
// takes to start playing varies, so it is difficult to 
// syncronize arduino events with the playback.  By resetting
// the cmdStartTime, this variance is eliminated.
// 2) WAIT_UNTIL_DONE - first waits for the BUSY pin to go
// high and then waits for it to go low indicating the playback
// is complete.  It is necessary to first wait for the BUSY pin
// to go high because it takes some time (several hundred 
// milliseconds) after initiating playback before this happens,
// so we need to wait for this before we start checking for the
// BUSY pin to go low.
// 3) NO_WAIT - starts the file playing and immediately returns.
void play(int folder, int file, int wait) {
  printf_P(PSTR("Play from folder %d file %d with wait %d\n"), folder, file, wait);
  myDFPlayer.playFolder(folder, file);
  if (wait == WAIT_UNTIL_STARTS) {
    while (digitalRead(DFBUSY_P));   // It takes a bit before the busy pin goes active
    cmdStartTime = millis();         // Reset the sequence start time
  } else if (wait == WAIT_UNTIL_DONE) {
    while (digitalRead(DFBUSY_P));   // It takes a bit before the busy pin goes active
    while (!digitalRead(DFBUSY_P));  // Wait until not busy
  }
}