Showing posts with label MP3 Player. Show all posts
Showing posts with label MP3 Player. Show all posts

Monday, September 14, 2026

DFPlayer library | MP3 audio player

Driver library for the DFPlayer Mini (MP3-TF-16P) MP3 audio player module. Depending on the module or version the main decoder IC can vary. Mine has the MH-ET LIVE MH3028M-24SS IC that is also compatible with ICs such as YX5200-24SS, YX5300, YX5300, YX6300.

The DFPlayer Mini includes an integrated audio decoder chip, a mono 8002A audio amplifier and an Micro SD card socket. The IC can decode WAV and MP3 formats and is used in serial-controlled audio players. It can be used as a standalone module together with an AD (Analog-to-Digital) keypad or driven by a microcontroller using serial commands. Even though the audio amplifier is mono, the module also has two pins that break-out the internal stereo DAC that can be connected to an external stereo audio amplifier or headphones.

The original board design was created by DFRobot, a well-known robotics and open-source hardware vendor based in Shanghai. 


 

Contents


Library characteristics

  • Language: C++
  • Supported devices: AVR. Developed on ATmega328PB. Supports Class 1 AVR (UDPI) devices. 
  • Dependencies: UART library for serial interface.
 

DFPlayer Mini characteristics

  • Sampling rates (kHz): 8/11.025/12/16/22.05/24/32/44.1/48.
  • Supported Formats: MP3, WAV, WMA. 
  • DAC output: 24-bit, support for dynamic range 90dB, SNR support 85dB.
  • File system support: Micro SD (up to 2GB) and Micro SDHC (up to 32GB) formatted in FAT16 or FAT32, support 32G USB disk, 64M bytes NORFLASH.
  • Control interfaces: IO, Serial Port (RX/TX), AD Key.
  • Special functions: the audio can be suspended by an advert then automatically resumed when advert is over.
  • Volume and EQ: 30 level adjustable volume, 6-level EQ presets (Normal, Pop, Rock, Jazz, Classic, Base).
  • Power supply: DC 3.2~5V.
  • Current: Sleep mode < 10mA, Playback 500mA max .
  • Output Power: 3W (5V Power Supply).
  • Size: 20mm x 20mm.
 

DFPlayer Mini pinout

DFPlayer Mini pinout
Source: https://wiki.dfrobot.com/dfr0299

Tip: the module can be used on a breadboard placed in the middle but pins one one side must be pushed towards the center with some pliers.

VCC: input voltage. DC 3.2-5V. Recommended 5V.

RX: UART serial input. Add a 1kΩ resistor between MCU TX and DFPlayer RX for signal conditioning. Internally DFPlayer Mini operates at ~3.3V and when MCU uses 5V the resistor helps with level compatibility and reduces noise.

TX: UART serial output.  Connects to MCU RX.

DAC_R and DAC_L: DAC audio output right and left channels are broken out directly to the module header pins for driving high-impedance loads (e.g., headphones or external audio amplifiers like an AUX line-in). Internally on the PCB, DAC_L and DAC_R are also mixed down to a mono signal and fed into the input of the 8002A (a Bridge-Tied Load / BTL mono power amplifier). The differential outputs of the 8002A go directly to SPK_1 and SPK_2.

SPK_1 (+) and SPK_2 (-): Outputs from the onboard 8002A mono audio amplifier. Direct connection for a passive speaker (≤ 3W output). You can connect either an 8 Ω or 4 Ω speaker. An 8 Ω speaker (3W) is usually the safest and most efficient choice. With a 4 Ω speaker it will deliver closer to the maximum 3W output, but it draws significantly more peak current (up to ~500mA–800mA). If you use a 4 Ω speaker, ensure your power supply can handle the higher current surges without causing the AVR microcontroller or the DFPlayer to brown out.

Warning: Because the 8002A uses a Bridge-Tied Load (BTL) output, neither SPK_1 nor SPK_2 is connected to Ground (GND). Never connect SPK_1 or SPK_2 to an external powered amplifier or to system ground, or you will short out the 8002A output drivers. Use DAC_L, DAC_R, and GND for external amplification.  

GND: power ground.

BUSY: playing status. Low means playing, High means idle. Optionally, it can be connected to an MCU digital pin.

USB- and USB+: The USB- (D-) and USB+ (D+) pins serve two distinct functions depending on how they are configured:

USB Host (Reading USB Flash Drives): If you connect a standard USB Type-A female port (with 5V power) to these pins, the decoder chip acts as a USB Host. It can read and play MP3 audio files directly from an external USB flash drive instead of the MicroSD card.

USB Device (PC Card Reader / Sound Card Mode): If you connect these pins directly to a PC's USB port, the module presents itself to the computer as a USB Mass Storage Device (allowing you to write files directly to the MicroSD card over USB without removing it) or as an External USB Audio Device. 

ADKEY_1 and ADKEY_2: DFPlayer Mini can operate in a standalone mode using resistive ladder button matrices (AD Key Mode) without needing UART commands. The module has two analog key inputs: ADKEY_1 and ADKEY_2. 
Each ADKEY pin supports a series of push-buttons connected through different resistor values to Ground. When a button is pressed, the voltage divider tells the module which "Segment" group action to execute: 
Segment 1 (ADKEY_1 Group): controls actions and playback triggers for the first set of 10 pre-defined tracks/folders (e.g., direct-trigger tracks 1 through 10 depending on which resistor/button is pressed).
Segment 5 (ADKEY_2 Group): controls actions for the fifth set/group of tracks (e.g., direct-trigger tracks 11 through 20 or higher folder groups depending on the module firmware matrix).
When controlled via software over UART these can be left floating/unconnected.

IO_1: trigger port 1. Short press to play previous. Long press to decrease the volume.

IO_2: trigger port 2. Short press to play next. Long press to increase the volume.

Wiring

Controlling the DFPlayer module using I/O mode

Controlling the DFPlayer module using I/O mode
Source: https://wiki.dfrobot.com/dfr0299

Short time press means prev/next, and long time press means vol- , vol+.

Controlling the DFPlayer module using AD mode

Controlling the DFPlayer module using AD mode
Source: https://wiki.dfrobot.com/dfr0299

They also sell the AD keypad as a module here.

External headphone circuit

DFPlayer external headphone circuit
Source: YX5200-24SS datasheet
Here R4 and R5 are current limiting resistors to prevent excessive external audio amplitude (Vp-p A maximum of 3.0V). To improve system stability, C5 and C6 blocking capacitors are used to prevent external audio sources affecting the DC level of the internal chip.

Serial Interface

Interface with the audio decoder IC is done using TX/RX UART pins. UART must be configured as follows:

Asynchronous mode, 9600 baud rate, data bits: 1, checkout: none, flow control: none.

Serial Control Protocol

Every frame sent to or received from the DFPlayer Mini consists of 10 bytes:

Byte Field Description / Value
0 Start Byte 0x7E
1 Version Always 0xFF. Not really used.
2 Length 0x06. Number of payload bytes that follow, excluding checksum & end byte.
3 Command CMD byte (e.g., 0x3F, 0x42, 0x03)
4 ACK 0x01 (Request reply frame) or 0x00 (No reply required)
5 Param High High byte of 16-bit argument ((param >> 8) & 0xFF)
6 Param Low Low byte of 16-bit argument (param & 0xFF)
7 Checksum High (checksum >> 8) & 0xFF
8 Checksum Low checksum & 0xFF
9 End Byte 0xEF

Checksum Calculation Formula

The 16-bit checksum is calculated by taking the 1's complement of the sum of bytes 1 through 6:

Checksum = 0 - Version + Length + CMD + ACK + Param_H + Param_L

Serial control commands

CMD Description Parameter
0x01 Play next
0x02 Play previous
0x03 Play track by FAT index. Commands 0x01, 0x02, 0x03 uses FAT indexing which means it doesn't read the file number but it uses the order in FAT table that depends on which order the files were copied.
0x04 Increase volume by 1
0x05 Decrease volume by 1
0x06 Set volume 0-30
0x07 Set EQ 0-5 (Normal, Pop, Rock, Jazz, Classic, Bass)
0x08 Playback mode. Configures global loop mode policies. E.g., automatically repeat the whole SD card, repeat a single folder, or play randomly).
Note: This command and other similar is not implemented by the library because on some modules like the one I have, the function is confusing or not supported. For example Shuffle/Random command will also play files in ADVERT folder. The most reliable way to repeat a track or a folder is to do it on the microcontroller.
0-3 (Repeat, Folder Repeat, Single Repeat, Random)
0x09 Playback sources.
U (0x00 / 0x01): USB Flash Drive (plugged into USB+ / USB-).
SD (0x02): MicroSD Card (TransFlash card).
AUX (0x03): External Analog Line-In audio mode.
SLEEP (0x04): Low-power standby mode.
On YX5200 chips, this unmounts active media (SD/USB) and puts the drive interface to sleep. However, on many JL/Jieli clone chips, changing the source to 0x0004 often fails or gets ignored if no active playback source was running.
FLASH (0x05): On-board USB SPI-Flash memory chip (if populated on board).
1-5 (U, SD, AUX, SLEEP, FLASH)
0x0A Standby. Goes to sleep for low power consumption
0x0B Normal Operation / Wake Up.
Exit Low Power / Sleep mode and return to normal working state.
Release Standby : If the player was put into standby mode, 0x0B resumes standard operation without performing a full hardware reboot (unlike reset command 0x0C).

0x0C Reset.
0x0D Play. Resumes playback.
0x0E Pause.
0x0F Play using folder and file name. The chip hardcodes its directory parsing rules to look for two-digit decimal string names: 01 through 99.
Folder: folders 01-99.
File name format must be 3 digits in length. E.g.: 001.mp3.
Valid range: folders 1-99, tracks 1-255.
Combines folder (01-99) into High Byte and track (001-255) into Low Byte
0x10 Analog gain trimming / volume normalization.
DH (Data High / Byte 5): Acts as an Enable/Disable switch.
- DH = 1 (0x01): Enables manual gain adjustment overriding master volume.
- DH = 0 (0x00): Disables manual gain adjustment.
DL (Data Low / Byte 6): Sets the fine gain level from 0 to 31.
For standard audio volume control, ignore command 0x10 and use Command 0x06 (Set Volume 0-30) instead.
0x11 Single-Track Repeat Toggle
Immediately forces the currently playing track to repeat continuously.
1 = loop current file, 0 = stop looping current file
0x12 Play files located inside a folder named /MP3/. File names must be 4 digits in length. E.g.: 0001.mp3. Valid range: tracks 1-9999.
0x13 Play an interruptive advertisement file from /ADVERT/ folder (0001.mp3 - 9999.mp3). Pauses current playback, plays advert, and auto-resumes original track when finished. A track must currently playing for the advert to play. Valid range: tracks 1-9999.
0x14 Play large folder. Folder number can be any integer from 1 to 15.
Track number can be any integer from 1 to 4095.
File names must be 4 digits in length. E.g.: 0001.mp3.
Top 4 bits = Folder (1-15), Bottom 12 bits = Track (1-4095)
0x15 Manually stop the advertisement early and return to main track playback.
0x16 Stop all audio playback.
0x17 Continuously play a folder. Not widely supported. Folder number [01-99].
0x18 Shuffle. Not widely supported.
0x19 Single cycle play. Not widely supported. 0: start play.
1: stop play.
0x1A Set DAC. 0: start up DAC output.
1: DAC no output.

Serial query commands

CMD Description Parameter
0x3F Send initialization parameters.
On non-YX5200 modules, the 0x3F is ignored when sending and is sent by the module only on power-up or on reset. When sent to the module (Query Online Storage):
Sending 0x3F (with arg = 0x0000) forces the module to return a bitmask of all currently available storage media.
When received from the module (Initialization Notification):
When the DFPlayer Mini boots up or resets, it spontaneously transmits 0x3F with a parameter mask indicating online storage devices.
0x01 = USB Flash Drive detected.
0x02 = MicroSD Card detected.
0x03 = Both USB and SD detected.
0x40 Incoming error frame sent from the DFPlayer. The parameter low byte (Byte 6) indicates the error code. 0x01 = Module busy
0x02 = Sleep mode
0x03 = Checksum error
0x04 = File not found
0x41 ACK (Acknowledge) response.
0x42 Query status. Parameter High:
0x00: Stopped / Idle (Ready for new commands)
0x01: Playing
0x02: Paused
0x43 Query volume.
0x44 Query EQ.
0x45 Query playback mode.
0x46 Query software version.
0x47 Query the total number of U-disk files.
0x48 Query the total number of TF card files.
0x49 Query the total number of TF flash files.
0x4A Queries the total number of tracks inside a specific folder. Not widely supported.
The High Byte is 0x00 and the Low Byte is the target folder number (e.g., parameter 0x0001 queries folder 01).
Returns a frame with command 0x4A where the 16-bit parameter represents the total file count in that specific folder.
0x4B Query the current track of U-disk.
0x4B Query the current track of U-disk.
0x4C Query the current track of TF card.
0x4D Query the current track of TF flash.

Library

Class constructor

DFPlayer(UARTstruct_t* uartInstance, volatile uint8_t* busyPORT, uint8_t pinBit);
DFPlayer(UARTstruct_t* uartInstance, volatile PORT_t* busyPORT, uint8_t pinBit);

uartInstance

UART object such as &uart0 to use UART module 0 or &uart1 for UART1.

busyPORT

Port where the BUSY pin is. E.g. &PORTC. If not used pass nullptr.

pinBit

Pin number on the port, e.g. PC0. If not used set it to 0.

Public methods

Initialization

bool init(uint8_t* playbackDevices)

Initialize UART and verify module readiness with up to 3 retries.

playbackDevices 

Pointer to an 8-bit variable to store detected playback devices. 0x01=USB, 0x02=SD, 0x03=SD and USB.

Return

True on success. 

Read BUSY pin

bool isBusy()

Read the BUSY pin on DFPlayer Mini. LOW = Playing, HIGH = Idle. Must be used after the command has been transmitted by the UART and the ACK has been received since only then the pin will be LOW.

Return

Pin state as true or false.

Play track by index

void playTrack(uint16_t trackNumber)

Global Track Player (CMD 0x03)
- Folder: root.
- File names must be 4 digits in length. E.g.: 0001.mp3.
- Valid range: tracks 1-2999.

Play the specified track number. Uses FAT indexing which means it doesn't read the file number but it uses the order in FAT table that depends on which order the files were copied. Same with playNext() (0x01) and playPrevious() (0x02) they are all global cluster navigators. 

trackNumber

Track number can be any integer from 1 to 2999.

Play next

void playNext(void)

Play next track.

Play previous

void playPrevious(void)

Play previous track.

Play folder track

void playFolderTrack(uint8_t folder, uint8_t track)

Standard Folder (CMD 0x0F)
- Folder: folders 01-99.
- File name format must be 3 digits in length. E.g.: 001.mp3.
- Valid range: folders 1-99, tracks 1-255.
 
Play a track by filename number in the specified folder.

folder 

Folder number can be any integer from 1 to 99.

track 

Track number can be any integer from 1 to 255.

Play folder track large 

void playFolderTrackLarge(uint8_t folder, uint16_t track)

Large Folder (CMD 0x14)
- Folder: folders 01-15.
- File names must be 4 digits in length. E.g.: 0001.mp3.
- Valid range: folders 1-15, tracks 1-4095.
 
Play track from folder using 4-digit filenames (0001.mp3 - 4095.mp3).

folder 

Folder number can be any integer from 1 to 15.

track 

Track number can be any integer from 1 to 4095.

Play MP3 folder

void playMP3Folder(uint16_t track)

MP3 Folder (CMD 0x12)
- Folder: /MP3/.
- File names must be 4 digits in length. E.g.: 0001.mp3.
- Valid range: tracks 1-9999.

track 

Track number: 1-9999.

Play advert

void playAdvert(uint16_t track)

Play an interruptive advertisement file from /ADVERT/ folder (0001.mp3 - 9999.mp3). Pauses current playback, plays advert, and auto-resumes original track when finished.
 
A track must currently playing for the advert to play.

track 

Track number: 1-9999.

Stop advert

void stopAdvert()

Manually stop the advertisement early and return to main track playback.

Stop play 

void stopPlay()

Stop all audio playback.

Play 

void play()

Resume playback.

Pause

void pause()

Pause playback.

Set volume

void setVolume(uint8_t volume)

Set volume.

volume 

Integer from 0 to 30.

Increase volume

void volumeIncrease()

Increase volume by 1.

Decrease volume

void volumeDecrease()

Decrease volume by 1.

Set EQ

void setEQ(uint8_t eq)

Set EQ preset.

eq 

Integer from 0 to 5 or one of following macros:

DFP_EQ_NORMAL
DFP_EQ_POP
DFP_EQ_ROCK
DFP_EQ_JAZZ
DFP_EQ_CLASSIC
DFP_EQ_BASS

Set playback source

void setPlaybackSource(uint8_t source)

Set playback source.
U (0x00 / 0x01): USB Flash Drive (plugged into USB+ / USB-).
SD (0x02): MicroSD Card (TransFlash card).
AUX (0x03): External Analog Line-In audio mode.
FLASH (0x05): On-board USB SPI-Flash memory chip (if populated on board).

source

Integer from 1 to 5 (U, SD, AUX, SLEEP, FLASH) or one of following macros:

DFP_PLB_DEVICE_USB_FLASH
DFP_PLB_DEVICE_SD_CARD
DFP_PLB_DEVICE_AUX
DFP_PLB_DEVICE_SPI_FLASH // if present on board

Sleep mode

void enterSleep()

Puts the audio decoder IC into low-power standby/sleep mode.

Exit sleep mode

void exitSleep()

Exit standby and resume normal operation without performing a full reboot.

Reset

void reset()

Reset IC.

Check event 

bool checkEvent(DFPEventData_t* eventData)

Universal non-blocking event handler that can be used in the main loop. Polls UART for spontaneous notifications (Track finished, Card inserted/removed, Errors).

eventData 

Pointer to a structure that will be populated with event details. 

Return

True if a valid event occurred, false otherwise.

Query status

uint16_t queryStatus()

Query current playback state.

Return 

0 = Idle, 1 = Playing, 2 = Paused, 0xFFFF = Query Failed.

Query software version 

uint8_t querySoftwareVersion()

Return

Version number. E.g. 8.

Query EQ

uint8_t queryEQ()

Return

Preset number.

Query volume

uint8_t queryVolume()

Return

Number representing current volume from 0 to 30.

Query playback mode

uint8_t queryPlaybackMode()

Return

0-3 (Repeat, Folder Repeat, Single Repeat, Random). Not widely supported.

Query total SD files

uint16_t queryTotalFilesSD()

Query total number of files on the SD card.

Return 

Number of files.

Query current SD track

uint16_t queryCurrentTrackSD()

Query current track playing from SD card.

Return 

Track number.

Code example

This example include UART debugging outputs that should be removed/commented out during production. UART0 is used to program the microcontroller over bootloader while UART1 is connected to DFPlayer RX/TX pins.

#include "DFPlayer.h"

// Define F_CPU in project settings to be globally accessible and easier to maintain.
// See: https://www.programming-electronics-diy.xyz/2024/01/defining-fcpu-in-microchip-studio.html
#ifndef F_CPU
    #warning	"F_CPU not defined. Define it in project properties."
#elif F_CPU != 16000000 // replace with your actual frequency
    #warning	"Wrong F_CPU frequency!"
#endif

int main(void) {
    // Local variables
    uint8_t ret_code = 0;
    uint8_t current_folder = 1;
    uint8_t current_track = 1;
    uint8_t max_tracks_in_folder = 10; // Track count for current folder
    uint8_t playback_devices = 0;
    // uint8_t status = 0;
    DFPEventData_t eventData;

    // Create player
    DFPlayer player(&uart1, &PORTC, PC0);
    // On UPDI devices
    // DFPlayer player(&uart1, &PORTC, PIN0_bp); // or 0
    
    // Initialize player
    ret_code = player.init(&playback_devices);
        
    // Fetch static meta-data at startup
    uint8_t version = player.querySoftwareVersion();
    uint16_t totalFiles = player.queryTotalFilesSD();
    uint8_t currentEQ = player.queryEQ();
    uint8_t currentVolume = player.queryVolume();
    uint8_t playbackMode = player.queryPlaybackMode();

    // Print info
    if (ret_code) {
        UART_sendString(&uart1, "Module initialized!\n");
        UART_sendString(&uart1, "Detected playback devices:\n");
        if (playback_devices == 0x01) {
            UART_sendString(&uart1, "- USB\n");
        } else if (playback_devices == 0x02) {
            UART_sendString(&uart1, "- SD Card\n");
        } else if (playback_devices == 0x03) {
            UART_sendString(&uart1, "- SD Card & USB\n");
        } else {
            UART_sendString(&uart1, "- None\n");
        }
        UART_sendString(&uart1, "Version: ");
        UART_sendInt(&uart1, version);
        UART_sendString(&uart1, "\n");

        UART_sendString(&uart1, "Files: ");
        UART_sendInt(&uart1, totalFiles);
        UART_sendString(&uart1, "\n");

        UART_sendString(&uart1, "EQ: ");
        UART_sendInt(&uart1, currentEQ);
        UART_sendString(&uart1, "\n");
        
        UART_sendString(&uart1, "Volume: ");
        UART_sendInt(&uart1, currentVolume);
        UART_sendString(&uart1, "\n");
        
        UART_sendString(&uart1, "Playback Mode: ");
        UART_sendInt(&uart1, playbackMode);
        UART_sendString(&uart1, "\n");
    } else {
        UART_sendString(&uart1, "Module initialization failed!\n");
    }

    // Wait for UART to finish sending info
    UART_isSending(&uart1, F_CPU, 9600);

    player.setVolume(20);
    player.playFolderTrack(3, 8);
    
    // Volume fade in effect
//    for (uint8_t i = 0; i < 26; i++) {
//        _delay_ms(50);
//        player.volumeIncrease();
//    }
    

    while (1) {
        // Query player status
//        status = player.queryStatus();
//        if (status == DFP_STATUS_IDLE) {
//            // Module is idle
//        } else if (status == DFP_STATUS_PLAYING) {
//            // Track is currently playing
//        } else if (status == DFP_STATUS_PAUSED) {
//            // Track is currently paused
//        }
        
        // Universal non-blocking event loop
        if (player.checkEvent(&eventData)) {
            switch (eventData.event) {
                case DFP_EVENT_TRACK_FINISHED:
                    UART_sendString(&uart1, "Track finished playing!\n");
                    // Wait for UART to finish sending
                    UART_isSending(&uart1, F_CPU, 9600);

                    // Auto-play next track or update app state here
                    current_track++;
                    if (current_track > max_tracks_in_folder) {
                        //current_track = 1; // Loop or advance folder
                    } else {
                        player.playFolderTrack(current_folder, current_track);
                    }
                    break;

                case DFP_EVENT_CARD_ONLINE:
                    UART_sendString(&uart1, "Card inserted\n");
                    break;

                case DFP_EVENT_CARD_OFFLINE:
                    UART_sendString(&uart1, "Card removed\n");
                    break;

                case DFP_EVENT_ERROR:
                    UART_sendString(&uart1, "DFPlayer Error received!\n");

                    if (eventData.errorType == DFP_ERR_BUSY) {
                        UART_sendString(&uart1, "Module is busy\n");
                    } else if (eventData.errorType == DFP_ERR_SLEEP) {
                        UART_sendString(&uart1, "Module is sleeping\n");
                    } else if (eventData.errorType == DFP_ERR_CHECKUSM_ERROR) {
                        UART_sendString(&uart1, "Checksum mismatch\n");
                    } else if (eventData.errorType == DFP_ERR_FILE_OUT_BOUNDS) {
                        UART_sendString(&uart1, "File not found globally\n");
                    } else if (eventData.errorType == DFP_ERR_FILE_MISMATCH) {
                        UART_sendString(&uart1, "Track index mismatch\n");
                    } else if (eventData.errorType == DFP_ERR_FOLDER_OUT_BOUNDS) {
                        UART_sendString(&uart1, "Track index does not exist inside the specified folder\n");
                    } else if (eventData.errorType == DFP_ERR_SD_FAIL) {
                        UART_sendString(&uart1, "SD card read/write failure or card removed during operation\n");
                    } else if (eventData.errorType == DFP_ERR_SLEEP_ENTRY) {
                        UART_sendString(&uart1, "Error entering low-power sleep mode\n");
                    }
                    break;

                default:
                    break;
            }
        }
    }

    return (0);
}


Links

Link includes:
- DFPlayer.h, DFPlayer.cpp
- uart.h, uart.c
- utils.h, utils.c
v1.0 DFPlayer
Other links

uart
Changelog
v1.0 12-09-2026
Public release under GNU GPL v3 license.