PowerBroker2 / DFPlayerMini_Fast

Fast and easy to understand Arduino library to use the DFPlayer Mini MP3 module from DFRobot.com. This is a huge improvement (both in terms of execution speed and simplicity) to the standard library provided by DFRobot.com.
200 stars 31 forks source link

isPlaying() always returns false #26

Closed morgan-wild closed 3 years ago

morgan-wild commented 3 years ago

Firstly, thank you for your work, I'm leaving the official library for some reasons you know, and yours seems a good option.

I'm looking for a way to know when a track is complete to manage a playing queue. For that, I used isPlaying(). The problem is this method always returns false, even when the module is playing.

Do you have any idea why? Regards.

PowerBroker2 commented 3 years ago

It seems I've misplaced my DFPlayer, so I can't help debug the code atm. However, maybe you can help. First, can you post your code in tags?

PowerBroker2 commented 3 years ago

In general, I always urge users to monitor the busy pin instead of using isPlaying()

morgan-wild commented 3 years ago

Thank you for your _Fast reply ;)

This is a simplified version of my code, it plays but I'm not able to know when the file completes. I tried to use the 'busy' pin immediately after I posted this ticket to have something functional fast. But that requieres one precious pin and it needs to let a delay (this pin is not LOW just after you call a play() method. It stays HIGH some ms then LOW and HIGH again when the file is complete).

#include <Arduino.h>
#include <SPI.h>
#include <DFPlayerMini_Fast.h>

#define PIN_PLAYER_RX       26
#define PIN_PLAYER_TX       27
#define PIN_PLAYER_BUSY     32
#define PROCESS_INTERVAL    1000

HardwareSerial      _playerSerial(1);
DFPlayerMini_Fast   _player;
unsigned long       _processTimer;

void setup() {

    // --------------------------------------------
    // Initilize serials

    Serial.begin(115200);

    _playerSerial.begin(9600, SERIAL_8N1, PIN_PLAYER_RX, PIN_PLAYER_TX);

    // --------------------------------------------
    // Initilize player

    Serial.print(F("Player: "));

    while (!_player.begin(_playerSerial)) {

        Serial.print(".");

        delay(1000);
    }

    Serial.println(F("Ready"));

    _player.playFolder(1, 1);
}

void loop() {

    // Temporized loop
    if (millis() - _processTimer < PROCESS_INTERVAL) {
        return;
    }

    // I know isPlaying() makes a request, that the reason I call it only 1 time per second
    // But it always returns false
    Serial.println(_player.isPlaying());

    _processTimer = millis();

}
PowerBroker2 commented 3 years ago

Are you using Serial for both MP3 communication and debugging? That's probably what's going wrong.

Also, if you want easy-to-use software timers, check out FireTimer.h

morgan-wild commented 3 years ago

I'm using 2 hardware serials, one for debugging and another for the DFPlayer (I'm using an ESP32). This setup is ok, I used the 'official' library with it.

Thank you for FireTimer, as I said, this code is a simplified version :)

PowerBroker2 commented 3 years ago

I'm guessing serial is defined by _playerSerial(1)? What architecture are you using?

What happens when you try this:

#include <Arduino.h>
#include <DFPlayerMini_Fast.h>

#define PROCESS_INTERVAL    1000

HardwareSerial _playerSerial(1);
unsigned long  _processTimer;

void setup {

    // --------------------------------------------
    // Initilize serial

    Serial.begin(115200);

    // --------------------------------------------
    // Initilize player

    Serial.print(F("Player: "));

    while (!_player.begin(serial)) {

        Serial.print(".");

        delay(1000);
    }

    Serial.println(F("Ready"));

    _player.playFolder(1, 1);
}

void loop() {

    // Temporized loop
    if (millis() - _processTimer < PROCESS_INTERVAL) {
        return;
    }

    // I know isPlaying() makes a request, that the reason I call it only 1 time per second
    // But it always returns false
    Serial.println(_player.isPlaying());

   Serial.println("Sent:");
   _player.printStack(_player.sendStack);

   Serial.println("Received:");
   _player.printStack(_player.recStack);

    _processTimer = millis();

}
morgan-wild commented 3 years ago

Hi @PowerBroker2 !

The traces look like:

Player: Ready
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
0

And loop like that even after the song completes to play. isPlaying() is always returning false. Best regards.

PowerBroker2 commented 3 years ago

Ok, looks like the stacks (aka packets) are being formed and communicated as expected. However, I just now realized that the datasheet shows the feedback byte cleared instead of set (i.e. my lib sets the 5th byte in the sent stack as 1, but is 0 in the datasheet). To see if it makes a difference, can you go into your local lib cpp file and edit line 942 to the following:

    sendStack.feedbackValue = dfplayer::NO_FEEDBACK;

If that doesn't work, add debug statements in parseFeedback() in the cpp. Namely, use something like this:

/**************************************************************************/
 /*!
     @brief  Parse MP3 player query responses.
     @return True if success, false if error.
 */
 /**************************************************************************/
bool DFPlayerMini_Fast::parseFeedback()
{
    while (true)
    {
        if (_serial->available())
        {
            uint8_t recChar = _serial->read();
            Serial.println(recChar, HEX); // <----------------- NEW DEBUG STATEMENT

            switch (state)
            {
            case find_start_byte:
            {
                if (recChar == dfplayer::SB)
                {
                    recStack.start_byte = recChar;
                    state = find_ver_byte;
                }
                break;
            }
            case find_ver_byte:
            {
                if (recChar != dfplayer::VER)
                    return false;

                recStack.version = recChar;
                state = find_len_byte;
                break;
            }
            case find_len_byte:
            {
                if (recChar != dfplayer::LEN)
                    return false;

                recStack.length = recChar;
                state = find_command_byte;
                break;
            }
            case find_command_byte:
            {
                recStack.commandValue = recChar;
                state = find_feedback_byte;
                break;
            }
            case find_feedback_byte:
            {
                recStack.feedbackValue = recChar;
                state = find_param_MSB;
                break;
            }
            case find_param_MSB:
            {
                recStack.paramMSB = recChar;
                state = find_param_LSB;
                break;
            }
            case find_param_LSB:
            {
                recStack.paramLSB = recChar;
                state = find_checksum_MSB;
                break;
            }
            case find_checksum_MSB:
            {
                recStack.checksumMSB = recChar;
                state = find_checksum_LSB;
                break;
            }
            case find_checksum_LSB:
            {
                recStack.checksumLSB = recChar;
                state = find_end_byte;
                break;
            }
            case find_end_byte:
            {
                if (recChar != dfplayer::EB)
                    return false;

                recStack.end_byte = recChar;
                state = find_start_byte;
                return true;
                break;
            }
            default:
                break;
            }
        }

        if (timoutTimer.fire())
            return false;
    }
}

Please let me know the results of these tests. I understand this is a bit of work for you, but I really appreciate your help, especially if it uncovers a bug for me to fix for all users.

morgan-wild commented 3 years ago

Hi! L942 is

int16_t DFPlayerMini_Fast::query(uint8_t cmd, uint8_t msb, uint8_t lsb) 😅

I added the Print you asked :

Player: Ready
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6 
42
0 
2 
0 
FE
B7
EF
7E
FF
6 
41
0 
0 
0 
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
Sent:
Stack:
7E FF 6 42 1 0 0 FE B8 EF
Received:
Stack:
7E FF 6 41 0 0 0 FE BA EF
7E
FF
6
42
0
2
0
FE
B7
EF
7E
FF
6
41
0
0
0
FE
BA
EF
0
PowerBroker2 commented 3 years ago

Ok, so the problem seems to be that the DFPlayer is returning 2 times per query, which is a big problem. The first response shows that a track is playing, however, the second response doesn't show this. I think if you change the line I asked (whatever line number it is - ours is different for some reason), I think it will only return one response per query and isPlaying() should be fixed.

The line to edit is the line in query() that says: sendStack.feedbackValue = dfplayer::FEEDBACK; Change it to: sendStack.feedbackValue = dfplayer::NO_FEEDBACK; Let me know what it prints out.

morgan-wild commented 3 years ago

Ok, it is line 947 (in my VSCode and Github 😉)

So it returns always True (1) now:

Player: Ready
7E
FF
6
42
0
2
0
FE
B7
EF
1  <------------------------------------------------
Sent:
Stack:
7E FF 6 42 0 0 0 FE B9 EF
Received:
Stack:
7E FF 6 42 0 2 0 FE B7 EF
7E
FF
6
42
0
2
0
FE
B7
EF
1
Sent:
Stack:
7E FF 6 42 0 0 0 FE B9 EF
Received:
Stack:
7E FF 6 42 0 2 0 FE B7 EF
7E
FF
6
42
0
2
0
FE
B7
EF
1
Sent:
Stack:
7E FF 6 42 0 0 0 FE B9 EF
Received:
Stack:
7E FF 6 42 0 2 0 FE B7 EF
7E
FF
6
42
0
2
0
FE
B7
EF
1
PowerBroker2 commented 3 years ago

Should be resolved in 1.1.12. Thanks!

morgan-wild commented 3 years ago

Closed? Do you found something? :)

PowerBroker2 commented 3 years ago

Yeah, I think I fixed it - try your original code with the new release

morgan-wild commented 3 years ago

Mmm I updated the library and checked your changes:

https://github.com/PowerBroker2/DFPlayerMini_Fast/compare/1.1.11...1.1.12

And as I said, with this fix, isPlaying() always returns true now :/ I will keep my method using a pin for the moment, I see it is not new: https://github.com/PowerBroker2/DFPlayerMini_Fast/issues/24

PowerBroker2 commented 3 years ago

Even when there's nothing playing? For the sake of simplicity, only test by playing a single track, not an entire folder

morgan-wild commented 3 years ago

Always. I'm playing a single track during my tests.

PowerBroker2 commented 3 years ago

in that case, I think your DFPlayer is defective. If it isn't, the DFPlayer's firmware must have a bug

morgan-wild commented 3 years ago

Hi @PowerBroker2

I tried with another DFPlayer from another manufacturer (marking are differents). And it is the same thing :s If you are sure your library is right, I'm agree to say the bug is in the DF's Firmware. I will make a try with the isPlaying() method of the official library to be sure.

PowerBroker2 commented 3 years ago

Interesting...That's a good idea - I'm very much interested in if the official lib works, too. Specifically, if it does work with the other lib, I'd like to see each byte (in hex) that is both sent and received from the DFPlayer. Perhaps my lib is malforming the packet.

agfaps commented 3 years ago

Hi, great library, thanks for the work. I stumbled upon the same issue. isPlaying() return false while playing a file. Because my program check this, if the return is true, then play advertisement. If not, then it plays from standard folder and file. When isPlaying return false while playing a file, instead of play advertisement, my program just play file from folder. After interrupting file is finished, it stays played indefinitely. The thing is, when I use module with name: DFPlayer Mini with AA20HFJ616-94 chip. Your library just working fine. When I use module with name: MP3-TF-16P with AS20HGJ548-74 chip, isPlaying() always return false. I have 3 modules with MP3-TF-16P name on it and the behavior is the same. I also have 2 modules with DFPlayer Mini name on it and it works fine. I don't know if this will add some insight for someone who will get the same experience as me. Thank you.

morgan-wild commented 3 years ago

That is very interesting @agfaps !

It is something to write in a disclamer in the readme file of this project. In this case the solution is to use the busy pin.

agfaps commented 3 years ago

I choose this library because the availability of isPlaying() function. So I don't need to use one precious pin from esp32. Next time I have to make sure to buy DFPlayer Mini with AA20HFJ616-94 chip from aliexpress/alibaba so I can still use isPlaying() function. BTW, I am still investigating this issue. I hope I can add more insight if there are more findings. Cheers.

morgan-wild commented 3 years ago

@agfaps I had the same story!

And recently, I removed the DFPlayerMini from my circuit and added a 24bits DAC. The sound is direcly played by the ESP32, the sound quality is better, it is possible to access the SD card, play a MP3 or other type of file from the network...

The DFPlayerMini and this library are useful for low power controllers with tons of pins :)

agfaps commented 3 years ago

Ah, great insight! For current project, I am stuck with dfplayer mini module because of its low cost(it was cheap clones because not using YX5200-24SS chip) and has built in amp ready to drive 4/8ohm 3-5W speaker. Next one I can try your solution also with esp32. Thank you. :)

T-vK commented 3 years ago

If anyone of you has more information on which chips work/don't work, I would appreciate the information. Here are a bunch of modules using different chips: https://github.com/PowerBroker2/DFPlayerMini_Fast/issues/18#issuecomment-762789856

So far, from what I read it seems like:

I'd also like to know where to buy one with a AA20HFJ616-94 chip because I couldn't find any offers for that one.

morgan-wild commented 3 years ago

I confirm

agfaps commented 3 years ago

Hi @T-vK, I have take a picture of working module with DFPlayer Mini name: WhatsApp Image 2021-01-19 at 20 56 39 WhatsApp Image 2021-01-19 at 20 57 54 The MP3-TF-16P module that need some workaround(read BUSY pin, and stopPlaying() after pausing a file before playing another file) regarding isPlaying():

WhatsApp Image 2021-01-19 at 21 04 45 WhatsApp Image 2021-01-19 at 21 04 45 (1)

T-vK commented 3 years ago

Thank you! Any chance you can tell me where I can buy them?

agfaps commented 3 years ago

I buy them on local online store in my country (Indonesia). Actually, I have buy several DFPlayer Mini module from two online store. Last time I bought the module again from those two online store, it was MP3-TF-16P module. So I think even from two local online store in my country, they get the module from one supplier. For my current project, I plan to buy directly from aliexpress/alibaba in a large quantity. My friend have experience that even buying large qty from one supplier in aliexpress/alibaba, you will get mix IC. Because of this, I decide to use workaround that will work on DFPlayer Mini or MP3-TF-16P module with those 2 IC's. And if the module that came is having a totally different IC, then I will try another workaround or try different libs. After digging in aliexpress, I found this link with IC that working last time I check: https://www.aliexpress.com/item/32954651185.html Please ask the seller to give evidence of the chip, and I hope it will work for you too.

PowerBroker2 commented 3 years ago

I finally got a new DFPlayer for testing and it turned out to be one that had the above problems with queries. I did some messing around and I fixed the issue for my DFPlayer chip and hopefully for the others, too.

To help facilitating debugging, I've added a debug option in the begin() method: https://github.com/PowerBroker2/DFPlayerMini_Fast/blob/0fb2a5b43cb03bb52e0f6ca7ba889c71179ed990/src/DFPlayerMini_Fast.h#L151 If you have an issue going forward, please provide the debug prints from the debug mode.

Please let me know if this new release (1.2.0) fixes things for yall!

HankLloydRight commented 2 years ago

I'm having this same problem with these knock-off boards not working! I've been buying these rouge DFplayers for years on Aliexpress with never a problem. I'm now on my second batch (one from Amazon and one from Ali) that just don't work right.

The Amazon batch were the MP3-TP-16P and a MH2024K chip. image The Ali batch has DFPlayer Mini HW24-A on the topside and a GD3200B chip image And now AliExpress rejected my refund claim (it's only $8 so no big deal), but still.

I just ordered a third batch of what I hope are genuine DFRobot versions from Digi-Key for a lot more dough than the knock-offs (about $8/each incl tax, tarrif, and shipping). Link: https://www.digikey.com/en/products/detail/dfrobot/DFR0299/6588463

What happened here, and why are there suddenly so many different cheap knock-offs that are incompatible with the existing libraries?

agfaps commented 2 years ago

@HankLloydRight I have read from many forum that YX5200-24SS are the original chip in dfplayer module. Now the original chip are not fabricated anymore, so dfplayer module producer have to use alternative chip that is not 100% compatible with the original. This affect in either with performance, or from serial command and response. Those two chips in your picture also sold in my country. One of the solution to make it work is to comment/disable 2 byte CRC when sending command to dfplayer. Hope it work in your situation. Regards.

HankLloydRight commented 2 years ago

Ah, I see, thanks for the reply.

And wow, that fix was so easy, and it works with the GD3200B chip! I just commented out these two lines in the sendData() function in DFPlayerMini_Fast.cpp:

    _serial->write(sendStack.checksumMSB);
    _serial->write(sendStack.checksumLSB);

Thank you so much! And a huge thanks to PowerBroker2 for this library!

PowerBroker2 commented 2 years ago

This library has become quite a pain in the neck for me to debug with all the BS china chips in circulation - would anyone be willing to test if other chips become unresponsive if I were to disable checksums by default? I would still have an option in begin() for the user to re-enable checksums.

HankLloydRight commented 2 years ago

I only have one set of fake DFPlayers to test (GD3200B) and they mostly worked by disabling the checksums.
I was thinking of creating a global variable to do just what you suggested to toggle between the checksums on or off. Is there some way programmatically to try it both ways and check the results to detect if there is a genuine DFRobot chip or a fake one?

BTW, I received my genuine DFRobot chips today (about $8 each) and they work great. I was surprised to see the blue LED instead of red on all the other versions I've used.

agfaps commented 2 years ago

@PowerBroker2 Thank you for your library. Previously I have 8 different chips: a. YX5200-24SS (24 pin) b. MH2024K-24SS (24 pin) c. GD3200B (16 pin) d. AA20HFJ616-94 (24 pin) e. AS20HH5863-74 (24 pin) f. AA19HFF859-94 (24 pin) g. MH2024K-16SS (16 pin) h. AC21BP0A672-84A2 (24 pin)

Chips a to f can run with checksum disabled. Chip g won't function with or without checksum. Chip h can run but no finished playing event and the LED keep turning ON after file ended playing. Chip h also have the worst sound output from its built in amp.

Please check also number of pins on the chip. This information was collected after digging from many forums and youtube videos.

To sums all effort to make one firmware that can handle most chips:

  1. disable checksum (this can be default, and user can choose from begin method)
  2. put some delay between command (especially if you want to change volume)
  3. increasing timeout query from default (I think it was 100ms) into 500ms really helps when querying.

Thank you.

HankLloydRight commented 2 years ago

I've been running this problem down and just got a few geniune DFRobot chips from Digikey.

If I'm not mistaken, it appears that the genuine DFPlayer Mini boards will work just fine with the two checksum lines below commented out. I don't know why, but it appears to work. Perhaps other people can try this out and see if they get the same results.

// _serial->write(sendStack.checksumMSB);
// _serial->write(sendStack.checksumLSB);

But now I have a different problem with the GD3200B DFPlayer chip version -- I'm attaching a wire from the BUSY pin to the A0 analog input pin on an Arduino (to detect when the unit is playing a clip**), and when that wire is attached, and the A0 pin definitely set for INPUT, whenever I play a track it automatically repeats 7 to 10 times. Anyone seen this, and maybe understand why or has a workaround? This does not happen when using the genuine DFPlayer Mini board.

** I'm not using isPlaying() because when I add it, it adds 2k to the sketch size and consumes 400 bytes of heap memory (probably due to the query() function).

agfaps commented 2 years ago

@HankLloydRight Hi, GD3200B can be used with two checksum commented out or not. From my testing with many chips log, I can play one file and one time only without repeating by itself with GD3200B. To check if one play have already finished or not, you can use two strategy:

  1. Create a code to set a flag when playing a file and check BUSY pin whether playing is finished or not. if finished, clear this flag.
  2. Add code from another dfplayer library only for checking serial event (without querying). You can filter out 'is playing finished' with track number info also. I am using DFRobotDFPlayerMini library and you can use (with some modification) method bool DFRobotDFPlayerMini::available() and method void DFRobotDFPlayerMini::parseStack(). Call this two method from your program in a loop and make sure not interfering with command and query into dfplayer.

Regarding BUSY pin and repeated playing, I don't have a problem with BUSY pin using GD3200B. I only have problem with BUSY pin only with chip AC21BP0A672-84A2 (24 pin) where BUSY pin always HIGH after finished playing a file(the LED also always ON). FYI, I am using ESP32 so I can also use no.2 because flash size and memory can handle it. It might differ in your situation. Hope this help.

Regards

HankLloydRight commented 2 years ago

@agfaps Thanks for the reply. I was actually saying that the ORIGINAL DFPlayer Mini from DFRobot will work with the checksums in or out. I'd like to see if others have the same behavior. If so, the library can be changed to remove sending the checksums if using an original or clone chip.

I already have code to check the BUSY pin when playing on the GD3200B, but if I have the BUSY pin connected to Arduino Nano A0 pin, the GD3200B always repeats the track being played between 7 and 10 times. Nothing I can do or send will stop it. If I disconnect the BUSY pin, this behavior stops, but then I can't detect the end of the track (without adding a lot more code, which I don't have code space for).

agfaps commented 2 years ago

@HankLloydRight Can you post what chip are inside the original dfplayer mini module from dfrobot? If it was YX5200-24SS (24 pin) then I can confirm that with or without checksum it will work. EDIT: after checking DFRobot website, the chip are DFROBOT | LISP3. This is new information for me because many people who I know are saying YX5200-24SS (24 pin) are the original chip one. Since I never find dfplayer with DFROBOT | LISP3 in online store/marketplace in my country, then I have never able to test it.

Regarding BUSY pin, it is a strange behavior indeed. Is there any possibilities to test GD3200B using another type of arduino board, or creating test code only to pinpoint the actual problem? If the following command to stop playing unable to stop it, then it might be problem in the knock off module.

This kind of problem can be daunting and I was already trying another mp3 player module that have consistent performance and behaviour.

Regards.

HankLloydRight commented 2 years ago

Below is the genuine DFPlayer I have from Digi-key -- it is in fact LISP3. This chip also works with or without sending the checksum. So my proposal for Powerbroker2 is to just remove it, or make it a switchable option. I know why it exists, but doesn't really seem necessary.

View recent photos

As far as the GD3200B repeat problem, I bought enough LISP3 chips to complete my project, so I don't have a lot of time to go back and test the GD3200B versions. But if I do, I'll report back here. It's just a shame to waste five GD3200B boards because they won't work with the BUSY pin connected to an analog INPUT pin. I've tried two different Nanos with the same result.

HankLloydRight commented 2 years ago

Ok, I solved the track repeat problem with the GD3200B chips. It has something to do with the required delay between sending commands to the board. I had been using a delay(50) which worked well before, but I had to increase that to 200 and then the repeating stopped. Also, I think I was inadvertently sending the two commands below without a delay in-between, which was somehow triggering the tracks to repeat:

mp3.stopRepeat();
mp3.stopRepeatPlay(); 

But once I increased the delay after all DFPlayer commands, and added a delay between those two commands, it now works perfectly fine -- pretty much same as the genuine DFRobot DFPlayer units.

agfaps commented 2 years ago

@HankLloydRight Glad to hear that. I forgot to mention in my previous reply:

To sums all effort to make one firmware that can handle most chips:

  1. disable checksum (this can be default, and user can choose from begin method)
  2. put some delay between command (especially if you want to change volume)
  3. increasing timeout query from default (I think it was 100ms) into 500ms really helps when querying.

Point no 2: the delay can be up to 500ms depending on the type of chip used in knock off dfplayer.