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 more quickly from Amazon. 

Here I show how the player can easily be operated without the overhead of dealing with a 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.