Wednesday, September 28, 2016

Playing sounds from the Arduino with DFPlayer

In this article I describe how to connect the cheap DFPlayer mini module to an Arduino, to play sounds from mp3 or wav files. Up to 99x255=25245 files can be stored (folders 01-99, each with files 001.mp3-255.mp3) on microSD cards (formatted to FAT16 or FAT32) up to 32GB. The DFPlayer connects to the Arduino via a serial port (TX/RX). It can directly drive a single speaker, or be connected to the line-in of a stereo amplifier.
The module can be obtained from AliExpress or eBay for less than $2.- (if you can wait three or four weeks) or from DFRobot for $8.90. Here I show how the player can easily be operated without any library.

Introduction

There is some information on the web that I used. One article seems to be from the manufacturer, describing the module and its features (although not complete, and maybe with some errors). They also provide a library - which is, however, not really needed. A better performance and a better understanding of the module can be obtained by using the original commands (as shown below).
Another article gives instructions how to build a mp3 player using an Arduino with the DFPlayer module. The main text, and in particular the comments, provide more information for connecting the player, and how to avoid loud buzzing sounds, by using separate power supplies for the Arduino and the DFPlayer. However, the main circuit seems to have a bug, as the GND from the Arduino and the DFPlayer are not connected.
Another nice implementation is found here, together with some good links, and there is also a link to the datasheet. The most useful information that I found, was in the forum at banggood from Ype Brada.This includes a detailed list of all (verified!) commands, a detailed description of the different ways how the sound files can be stored on the SD card, plus explanations and example code how to use the module without an additional library. My example code below is mostly based on this last source, but my setup uses information from all sources on the electrical connections, to avoid hum and/or buzzing sounds.

Components

Here is what you need:
  • Arduino Uno (or other)
  • DFPlayer module (cheap from AliExpress or eBay - but expect to wait 3-4 weeks, or from DFRobot)
  • microSD card
  • speaker 
  • one diode 1N4001
  • two 1kOhm resistors
  • two or three capacitors 30-470uF
  • two or three capacitors 0.04-0.1uF
  • 7805 voltage regulator
  • 6 AA batteries (I use rechargeable ones, to get 6x1.2V=7.2V)
  • PAM8403 2x3W amp (only required if you want to drive stereo speakers)

I tried three different microSD cards and all worked for me: two no-name cards (128MB for $0.90 and 1GB for $1.80) and a 4GB card from Kingston. All of these worked out-of-the-box, and also when formatted to FAT16 or FAT32. The 128MB card gives plenty of storage for my applications (approx. 60 minutes for mp3 files - this is much more than all R2-D2 and MSE-6 sounds that I could find on the web), and it is cheap.

Circuit

The most critical aspect is to get the player running without
producing hum or buzzing sounds. From other comments, it seems to be essential to decouple the power supplies of the Arduino and the DFPlayer, and to slightly reduce the voltage for the latter from 5V (although the data sheet specifies a range of 3.3-5.0V, it also quotes a typical value of 4.2V which seems to work better). To operate my circuit with a single set of batteries, I decided on the following solution:

I am operating the Arduino from 6 rechargeable NiMH AA batteries (6x12.V = 7.2V) through the Vin pin and add a 7805 voltage regulator to provide power for the DFPlayer and the PAM84503 2x3W amp (the amp is not required, when operating a single speaker, which can be powered directly by the DFPlayer). Most capacitors in this circuit are not necessarily required - I would only make sure to have a larger one in the front (33-470uF) and keep the 0.1uF at the output of the 7805 voltage regulator. The TX/RX pins of the module are connected to the RX/TX of the Arduino via serial 1k resistors.
The test setup on the breadboard looks a little chaotic - but it works!
And it looks much better on a PCB.

Modes of Operation

The DFPlayer mini has three modes in which mp3 files can be played:
  • mode #1:
    The files are stored in the root directory of the SD card. The file names do not matter. Files are accessed based on the number corresponding to the order in which they were written to the card. This sounds weird - I agree. I would only use this if I had a small number of files, and the files are not likely to change.
  • mode #2:
    Files are stored in folders, numbered 01-99, with filenames 001-255.mp3 or 001-255.wav, so you can store up to 99x255=25245 files. The files are accessed based on folder and file number.
  • mode #3:
    Up to 2999 files are stored in the directory "mp3" with four-digit filenames 0001.mp3-2999.mp3
The following example uses mode #2, which seems to be the most flexible one for my purposes, as it allows to organize the mp3 files in different folders.
Although there exists a dedicated library DFPlayer_Mini_mp3.h, this is not really needed. One probably improves the performance (storage, speed) and gains a deeper understanding by using standalone code (which is only a single subroutine dfpExecute() at the end of my example).
The general command structure consists of 10 bytes of which five are fixed, two are a checksum, and three specify the action (one is the command and two are parameters)
byte    function         value
(0)     start byte       0x7E
(1)     version info     0xFF
(2)     number of bytes  0x06
(3)     command          0x..  <- the command specifies by the user
(4)     command feedback 0x00  (if no feedback is required)
(5)     parameter 1 [DH] 0x..  <- 1st parameter (high-byte) specified by user
(6)     parameter 2 [DL] 0x..  <- 2nd parameter (low-byte) specified by user
(7)     checksum high    0x..  <- calculation is given below
(8)     checksum low     0x..  <- calculation is given below
(9)     end command      0xEF
The checksum is computed inside "dfpExecute()) as
checksum = - ( byte(1)+byte(2)+byte(3)+byte(4)+byte(5)+byte(6) )
and byte(7) and byte(8) are obtained as
checksumHigh = highByte(checksum)
checksumLow = lowByte(checksum) 
In mode #2, the command to play a single file is byte(3)=0x0F, byte(5) is the folder number (01-99), and byte(6) is the track number (01-255). So, playing file iFile in folder iFolder, is done by: dfpExecute(0x0F,iFolder,iFile).
One can check if a file is currently playing, by sending the busy signal (pin 16 of the module) to a digital input of the Arduino, which is LOW while a file is playing, and HIGH if not.
I read somewhere two recommendations, to wait at least five seconds between powering the player on and off, and to wait at least 20ms between subsequent commands. This should not cause any problems in typical operation.

Example Code 

For this example you should use a microSD card with a folder "01" that contains a few mp3 files (named 001.mp3, 002.mp3, 003.mp3, ...). The number of files is specified in the loop() in variable "nfiles". The example cycles through all files (playing the next file when the BUSY pin is HIGH, after a short delay), and starts again after the last file was played (with an additional delay of 4 seconds).
The DFPLayer's BUSY pin signals when a mp3 file is currently playing (BUSY == LOW) or if not (BUSY == HIGH). However, as pointed out in the comments below, it takes the busy pin a little time (<200ms) to become LOW after the mp3 file started playing. So, make sure to wait a little before checking the BUSY pin.
 //  
 //  --- example code for using the DFPlayer module ---  
 //  
 // Notice:  
 // All of the information/code here is largely taken from a posting  
 // by user Yerke in the forum at:  
 //  http://forum.banggood.com/forum-topic-59997.html  
 //  
 // This is example code for using DFPlayer in mode #2 (microSD card   
 // with directories 01-99, with filenames 001.mp3-255.mp3)  
 // In this example we assume three files (001.mp3, 002.mp3, 003.mp3  
 // in directory "01" on the SD card.  
 //  
 // This example shows how to  
 // - init DFPlayer  
 // - set the volume  
 // - check the "busy" pin of the DFPlayer  
 // - repeatedly play three MP3 sound files with names 001.mp3, 002.mp3, 003.mp3  
 //  which are stored in directory "01" on the SDcard.  
 //  
 // **************************************************  
 // Very important:  
 // In many setups, the DFPlayer produces noise.
 // To fix this, one should do two things   
 // - The DFPlayer prefers voltages below 5V.  
 //   For me it worked, by connecting it in series with a 1N4001 diode to 5V.  
 // - One should also use serial 1K resistors to connect its TX/RX pins to   
 //   the RX/TX pins of the Arduino  
 //   
 // Connect:  
 // - DFPlayer TX (pin 3) to Arduino pin 10 via a 1k resistor  
 // - DFPlayer RX (pin 2) to Arduino pin 11 via a 1k resistor  
 // - DFPlayer Busy (pin 16) to Arduino pin 12  
 // - DFPlayer speaker output (pins 6,8) to a speaker  
 // - DFPlayer VCC (pin 1) to 5V through a 1N4001 diode  
 // - DFPlayer GND (pin 7 or 10) to GND of the power supply and to Arduino GND  
 // **************************************************  
 //  
 // Wait at least 5 seconds before powering down/up the DFPlayer  
 //  

 const byte pinDfpRX = 10;  
 const byte pinDfpTX = 11;  
 const byte pinDfpBusy = 12;  
 const byte dfpVolume = 0x11; // set volume of DFPLayer - in range: 0x00-0x30 - default=0x30  
 
 #include "SoftwareSerial.h"  
 SoftwareSerial mySerial(pinDfpRX, pinDfpTX); // RX, TX  

 void setup () {  
  Serial.begin(9600);       // for printout  
  mySerial.begin(9600);      // for communication with DFPlayer   
  pinMode(pinDfpBusy, INPUT);   // init Busy pin from DFPlayer (lo: file is playing / hi: no file playing)  
  dfpExecute(0x3F, 0x00, 0x00);  // Send request for initialization parameters  
  while (mySerial.available()<10) // Wait until initialization parameters are received (10 bytes)  
  delay(30);            // have >20ms delays between commands  
  dfpExecute(0x06,0x00,dfpVolume); // set volume DL=0x00-0x30, default=0x30  
  delay(30);            // have >20ms delays between commands  
 }  

 void loop () {  
  const byte nfiles = 3;     // number of mp3 files on SD card  
  static byte ifile = 1;      // number of file played next  
  if (digitalRead(pinDfpBusy) == HIGH) {    // if no mp3 is playing -> play next file  
   delay(600);  
   Serial.print("now playing file No: ");  
   Serial.println(ifile);  
   dfpExecute(0x0F,0x01,ifile); // play (0x0F) file [ifile] from folder "01"  
   delay(200);          // wait a little for the busy line to become LOW
   ifile++;  
   if (ifile > nfiles) {  
    ifile = 1;  
    delay(4000);        // add a little delay before repeating the loop  
   }  
  }  
 }  

 // --- Excecute the DFPlayer command with two parameters (folder and file numbers)
 void dfpExecute(byte CMD, byte Par1, byte Par2)  
 {   
  # define Start_Byte   0x7E  
  # define Version_Byte  0xFF  
  # define Command_Length 0x06  
  # define Acknowledge  0x00   
  # define End_Byte    0xEF  
  // Calculate the checksum (2 bytes)  
  uint16_t checksum = -(Version_Byte + Command_Length + CMD + Acknowledge + Par1 + Par2);  
  // Build the command line  
  uint8_t Command_line[10] = { Start_Byte, Version_Byte, Command_Length, CMD, Acknowledge,  
         Par1, Par2, highByte(checksum), lowByte(checksum), End_Byte};  
  // Send the command line to DFPlayer  
  for (byte i=0; i<10; i++) mySerial.write( Command_line[i]);  
 }  
For further projects, just copy and paste the function "dfpExecute" into your Arduino sketch, and you can use the DFPlayer without any additional library. The functionality from this example (play a specified file from a specified folder, and check the BUSY pin if it is still playing) should be sufficient for many Arduino projects.
If you want to use additional functionality, you should check the posting by Ype Brada and maybe get additional information from the data sheet

Additional Comments

Formatting

After using the DFPLayer mini in various projects, I can confirm that the module works well with audio files in different formats. So far I had success with .mp3 and with .wav files, in stereo and in mono, and with different sampling rates like 15kHz, 22.05kHz, 44.1kHz, and 48kHz. I stored these on 128MB micro SD cards as they arrived, without any formatting. In other words: You can use a store-bought micro SD card and copy all your existing files from your collection on it, without worrying about (re-)formatting the card and/or the sound files. I only recommend to normalize the sound files, so they play at maximum loudness.

Amplifier

The circuit shown above uses an additional 3W stereo amplifier (PAM8403). In my recent projects, however, I found that this is not really needed and I just used the integrated amp. This gives a very reasonable loudness which, in particular with a slightly larger speaker (3"-4"), should be sufficient for most indoor purposes (I never set it to max volume). Of course, this amp is only mono - but that's o.k. for my robots and my HAL 9000 replica.

Arduino Serial

My example code above uses the SoftwareSerial library. But the Arduino Mega has four hardware serial ports. Using a hardware serial saves the memory required by the SoftwareSerial library. When using Serial3 (with TX3, RX3 pins), one only needs to replace the three occurences of "mySerial" by "Serial3" (and to remove the two lines with "SoftwareSerial".)

Some Trouble

The code above always worked for me when using Arduino Unos or Megas. However, recently, I saw a problem when using an Arduino Pro Mini - the same problem that is mentioned in one of the comments below: The code gets stuck at this statement:
while (mySerial.available()<10) // Wait until initialization parameters are received (10 bytes) 
It does not do any harm to simply remove this statement (or maybe just comment it out).

Noise Issues

Many people are reporting noise issues. To avoid these, it seems that the following steps are essential
  • Do not operate the DFPlayer at 5V. Either use a separate power supply - or use the diode (as in the circuit above).
  • Insert resistors (e.g. 1k Ohm) in the TX/RX lines (as in the circuit above)
  • The DFPlayer has two GND pins (pins #7 and #10). These should always be connected by a short wire that goes directly from one to the other (it seems that I always did that in my circuits, but I did not indicate this in the circuit above). 

My Projects with the DFPlayer Mini

The setup and the basic code detailed above were successfully used in the following movie props that I built:
  • Star Wars, Mouse droid MSE-6 
  • Star Wars astromech droids R3-B9 and X7-OB, imperial droid H015
  • In my Flux Capacitor ("Back to the Future") the DFPlayer is used to play electric hum and buzz sounds 
  • In the Time Circuits ("Back to the Future"), the DFPlayer, plays the DFTM sounds for the keypad, the turn-on, turn-off, etc. sound clips, and a few songs from the movies.
  • HAL 9000 replica ("2001 - A Space Odyssey") - DFPLayer plays all of HAL's clips
  • In the planning phase: GERTY 3000 replica ("Moon")
If you want to use the DFPlayer to play sound clips from movies, I have a tutorial on how to rip the audio track from a DVD here.

26 comments:

Thomas Weeks said...

So I'm trying to use this darn thing via serial (with the newer DFRobotDFPlayerMini code) am powering the DFPlayer with 5v, am using the 1K resistors (to drop the arduino TX down to ~3.3v).. have inserted delay(20); before each command.. but when I issue a
Serial.println(myDFPlayer.readState());

I get back a -1

What's that mean?

Tweeks

Markus said...

Thomas, I would not operate the DFPlayer at 5V. Many people had trouble with that. I am using the 1N4001 diode to reduce the voltage by approximately 1V. In three different projects, this always worked for me.

mamvcivm said...

Hi Markus, would it be possible to connect dac l, dac r and gnd to the aux input of a typical domestic hifi amplifier, or would the levels be all over the place?

Thank you,

Andrew

Markus said...

Andrew, yes, this is exactly like the PAM8403 amp is connected - that should also
work for HiFi amps. Of course, to be careful, I would initially set the DFPlayer volume, and the amp volume to a very low level.

Féroce Lapin said...

Two points: with the DFPlayer I recently bought, there is no data coming back after the first dfpExecute code. So mySerial.available() stay at 0. Just comment the while() and it works.
Also, after the dfpExecute(0x0F,0x01,ifile); in the loop, you need to add a short delay (eg delay(200). Because the busy line is not set immediatly after the call. So if you test it just immediatlty after calling dfpExecute(0x0F,0x01,ifile); you get a wrong result, telling no mp3 is playing and so you just start to play the next file.
Hope this help!

Unknown said...

Markus- Thanks for the tips related to the 3 modes of operation. Yours was the first post I could find describing how to properly structure the folders and filenames so as to play files in folders other than /mp3.

Markus said...

Unknown, thank you for the feedaback! I'm glad this was helpful.

Markus said...

Féroce, I also once had a problem with the first dfpExecute. But then I found that this happened only due to interference of the serial connections, because the Arduino was still connected to my PC. Once I disconnected it, it worked.
And thank you for pointing out the feature of the delayed response of the busy signal. In my applications this never happened, probably because I am only checking the busy signal after longer time intervals.

Daniel W. said...

The Link to the Banggood Forum doesn´t work any more.
Here is a Link to a copy of his Posts:
https://web.archive.org/web/20150412161300/http://forum.banggood.com/forum-topic-59997.html

Or here from Pastebin:
https://pastebin.com/cwqBkPXW

Creston said...

Marcus,
THANK YOU SO MUCH!
After a couple weeks of research, your work was the most concise and functional application of the code. Thank you for posting your knowledge!

Markus said...

Thank you, Creston. I'm glad this was helpful for you. In the meantime I have used this in about ten different projects. It is such a flexible, cheap, and simple way to beef up any project with sound.

snaxau said...

The delay will only take effect when ifile > nfiles, not during next play.

if (ifile > nfiles) {
ifile = 1;
delay(4000); // add a little delay before repeating the loop
}

I think it is meant to be like:

if (ifile > nfiles) {
ifile = 1;
}
delay(4000); // add a little delay before repeating the loop

Markus said...

Thank you, snaxau. You are right, in my version of the code, the delay of 4sec is only applied after the last track, before it starts playing the first track again. This is what I wanted for this example. If you want to have a 4sec delay after each track, than your version is correct.
You could also combine both: e.g. add 2sec delay after each track (place delay(2000) where you suggested), plus additional 3sec after the last track is played (place delay(3000) where I put it).

Hayward Haunter said...

Do you really need the 1K resistor in both directions? If the TX on the DFPlayer Mini is using 3.3V logic, then a high should just barely register on the Arduino's RX. But if you put a resistor in there, you'll drop the voltage farther.

The resistor makes sense (to me) in the other direction, since you don't want the Arduino's 5V TX to directly drive the DF Player Mini's RX.

Markus said...

Hayward, thanks for your thoughts. This makes a lot of sense. But, as they say, "if it ain't broke, don't fix it". I have used this setup now for six projects - it always worked absolutely stable, and for this part of the circuit I don't do any test setups on breadboards anymore - I just copy it into the next project. Maybe in my next project I keep the resistor but add a bridge over it. If it does not work reliably, I can just cut the bridge. Or maybe you can try that and let me know.

Unknown said...

Dear Markus,

First of all, thanks for this article.

I have a similar set-up as yours, also with an extra amplifier.
I give 5v power to the DF-Player which is probably to much.
Would it suffice to just place a IN4001 diode between the + 5V and the VCC of the DF Player, or do I also have need a extra number of capacitors? When yes, where do I put them?

Regards
Benny

Markus said...

Benny, putting the 1N4001 diode between +5V and the VCC input of the DFPlayer is exactly what I described - and it always worked for me. It should work without any further capacitors, but a small one (100nF = 0.1uF - between GND and the player's VCC) would help to filter out noise. And larger one (100uF or 220uF, or more) would help to supply a little extra current at loud sound peaks. Otherwise, I experience that at loud sound peaks, the DFPlayer draws too much current, so the voltage goes down and the Arduino resets. A large capacitor would help to stabilize this (if it does not, then you need to reduce the volume).

Benny said...

Hi Markus,

Thank you for the quick and full response !!
I have here a IN4007 diode, I suppose I can also use this one.

Best regards,Benny

Benny said...

Hi Markus,

Thank you for the quick and full response !!
I have here a IN4007 diode, I suppose I can also use this one.

Best regards,Benny

Markus said...

Benny, yes, the 1N4007 works too.

skc351 said...

HI,
Is pull-down resistor required in between busy and Arduino pin??

Markus said...

skc351, no the busy line from the DFPlayer can be connected directly to the Arduino input. This worked in all my projects.

Hank said...

Markus, you are a genius! I've been banging my head for DAYS trying to figure out and fix the strange order the tracks were being played in. All the other docs online for DFPlayer conflict -- with some saying three digit file names (001.MP3), some with four digit names, some say files in the root directory, some in /mp3, and others with the /01../09 directory structure. But nowhere ELSE did anyone specify the three distinct playback modes and how to enable each one. Thank you! Thank you! Thank you!

Markus said...

Hank, thank you for your kind words. I'm glad it was helpful. I was also, initially, very overwhelmed by the different modes - that's why I thought the blog post might be helpful. Good luck with your project.

Hank said...

Markus, I found a quirk with the /mp3 mode #3.. the actual file name is irrelevant, it’s the sort order that counts.. if you have files:
0001.mp3
0002.mp3
0004.mp3
0009.mp3

You need to request Track 3 to play 0004.mp3 and request Track 4 to play 0009.mp3

So the 0001...000x is just to sort the files in ascending order, but the file names are not used for playback— it’s their physical order.

Markus said...

Thank you, Hank, for the additional information. This is almost as weird as in mode #1 where the order matters in which files are written to the card. I would use neither of them. Mode #2 allows to store 255 files in one directory. If I had more than 255 files, I would really want to organize them in different directories. But also in many other cases, for example for my Flux Capacitor. This makes different electrostatic noises which are organized in three categories (soft, medium, heavy). The sound clips for each category are stored in a separate folder, and within each folder they are accessed randomly. Here the directory structure is very helpful as I can always add files to individual directories so my code does not have to keep track of which files belongs to which category.