A great & very cheap MP3 sound module without need for a library!

Hello,

I took your code and modified it. Basically, I have 23 mp3 files on my SD card (001.mp3 through 023.mp3) and I’m looping through each one in order and then starting over. It seems to work just fine up to “011.mp3”, but then it starts over at “001.mp3” before it get’s the chance to play “012.mp3”. It doesn’t matter what mp3 files I use or the folder structure, so I’m assuming it’s not the MP3 module and there’s just a bug in my code that I’m not seeing. By the way, if I define MP3_END as 46, all 23 MP3 files play correctly, but that doesn’t make any sense. How can the count value be double what it should be, but each MP3 file is played in order? It’s really confusing.

Anyway, could you be a second set of eyes and take a look at it for me?

# define MP3_START 1
# define MP3_END 23


int mp3PlayerStatus = D6;

void sendCmd(int cmd, int lb, int hb, bool reply = false)
{                 // standard format for module command stream
    uint8_t buf[] = {0x7E, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF}; 
    int16_t chksum = 0;
    int idx = 3;                    // position of first command byte

    buf[idx++] = (uint8_t)cmd;      // inject command byte in buffer
    if (reply) buf[idx++] = 0x01;   // set if reply is needed/wanted
    if (hb >= 0)                    // if there is a high byte data field
        buf[idx++] = (uint8_t)hb;   // add it to the buffer
    if (lb >= 0)                    // if there is a low byte data field
        buf[idx++] = (uint8_t)lb;   // add it to the buffer
    buf[2] = idx - 1;               // inject command length into buffer
    
    for (int i=1; i < idx; i++)     // calculate check sum for the provided data
        chksum += buf[i];
    chksum *= -1;

    buf[idx++] = (chksum >> 8);     // inject high byte of checksum before
    buf[idx++] = chksum & 0xFF;     // low byte of checksum
    buf[idx++] = 0xEF;              // place end-of-command byte

    Serial1.write(buf, idx);        // send the command to module
    for (int i = 0; i < idx; i++)   // send command as hex string to MCU 
      Serial.printf("%02X ", buf[i]);
    Serial.println();
}

void waitForMp3ToFinish()
{
	// Give the busy pin some time to go low (MP3 file playing)
   	 delay(200);
            
   	 // Wait for the pin to go high again (MP3 file finished)
    	while(digitalRead(mp3PlayerStatus) == LOW)
    	{
        	Particle.process();    // Sit and spin
    	}    
}


void setup()
{
    Serial.begin(115200);
    Serial1.begin(9600);
    
    // Crank the volume to maximum (30)
    int cmd = 0x06;
    int hb = 0x00;
    int lb = 0x1E;
    sendCmd(cmd, lb, hb, true);
    
    // Set up the I/O
    pinMode(mp3PlayerStatus, INPUT);
    
}

void loop()
{
    int cmd = 0x03;
    int hb = 0x00;
    int lb = MP3_START;

    while(1)
    {
        
         
        Particle.process();
        
        while(lb < MP3_END)
        {
            Particle.process();
            
            // Send the MP3 command
            sendCmd(cmd, lb, hb, true);

            waitForMp3ToFinish();
            
            delay(1000);    // Add a little delay between files.
            
            // Go to the next track
            lb = lb + 1;
           
        }
        
        // Reset the counter and then wait an additional 4 seconds before playing from the beginning again.
        
	lb = MP3_START;
        delay(4000);	// Use a long delay to flag that it’s starting over.
                
    }
    

    
}

I look forward to your reply.

Thanks,

Corey

I’d have to dig up the module again, but IIRC I had to use a different play command to get this working as intended.

This was the project where I managed to get it play exactly the folde/file I intended to
Nextion display AND DFPlayer MP3 module
(the actual control code is running on the display tho’)

Thanks for the feedback.

I’m pretty sure the command structure and checksum calculation code is right, because I can adjust the volume without any issue. It’s selecting a specific MP3 file and playing it that’s causing me problems.

I was actually refering to the command code (other than 0x03) and not the command structure.

I think 0x03 requires four digit track numbers.

I successfully used 0x0F.

Thank you for your documentation i really appreciated :grin:

I’ve done a lot of testing with the DFPlayer (FN-M16P) this past week or so, and 0x0F appears to be the best command for playing tracks …

  • 0x0F plays tracks that are stored in folders

  • Reference folders (01-99)

  • References tracks within those folders by name (001-255.xxx), where xxx is “mp3” or “wav”.

  • You must specify both a folder and a track when using 0x0F

The 0x03 command does reference track by a uint16_t track number that spans the MSB and LSB bytes. The track number does not reference the tracks name. (I believe it references the order in which the tracks were added to the disk.)

Volume commands set/get/increase/decrease appear to work fine.

0x48 returns the number of files on the TFcard … tho the documentation says that command returns the number of files on Udisk.

Speaking of documentation … This document came from the manufacturer, and is the best reference I have found.
http://www.trainelectronics.com/Arduino/MP3Sound/TalkingTemperature/FN-M16P%20Embedded%20MP3%20Audio%20Module%20Datasheet.pdf

1 Like

I have good news for those who have been looking for a DFPlayer command to exit standby mode …

Command 0x09 performs that task.

7e ff 06 09 01 00 02 fe ef ef  exits standby with the SD card selected
7e ff 06 09 01 00 01 fe f0 ef  exits standby with a USB memory key selected
1 Like

I want to build this… it would be my second Particle project… I want to build a unit that has a Staples “Easy” button that you push. Each time you push the button I want it to play one sound file (always the same) and have a light bulb turn on for about 10 seconds. Is it possible with this code? How much would have to change? HELP!

Yes, It is quite easy. My recommendations are:

  • Start with an empty SD card, create a folder named ‘01’ and and store your MP3 file as as '001.MP3.
  • Use the 0x0F command to play the MP3 file.
  • Use the 0x06 command to adjust the volume.
  • Explore other commands later if curiosity takes you there …

Ok… I reckon I can handle that… however I want to try and get a little more complex… I will give you context…

I want to wire this to an “easy” button that plays a song AND activates a red spinning light for 10 seconds… It is going to be a “SALE” button for the office that gets pushed when someone makes a sale. Wiring to the speakers shouldn’t be hard.

The other project I built was a Google Assistant controlled garage door opener. I had to use a Photon high voltage relay to finish it. I assume I will need a high voltage relay for this as well… to activate the the light.

So, if my thought process is correct. I get the MP3 stuff wired up and coded with a command that correlates to where the button is connected. THEN I need to insert the code for also controlling a high voltage relay… and I should be able to write a simple piece of the code that uses the same button command, 0x0F, that will close the relay for 10 seconds. Will the particle tool for adding the high voltage relay drop right into the code provided for this? And I can only assume that using the same command to initiate two different things will be just fine…

you seem on the right track.

check out software timers to create a callback function to turn off your light

The dfPlayer can drive one small speaker in mono mode, or a pair of earbuds in stereo. You say “speakers” which tells me that you will need to add an amplifier to the mix.

I assume that you will need a HV relay for the light as well. I’m guessing “Photon high voltage relay” translates to Particle Relay Shield. I use one of them to control my garage door openers as well. Turning on a relay on and off is done with one line of code that either sets a specific Photon pin high or low. Triggering the DFPlayer to play a track is a simple matter of writing 10 bytes to Serial1. Since you plan to play the same track each time, you will always be sending the same 10 bytes, so very little code will be required.

if you compile this sketch, you can experiment with it until you get the 10-byte buffer that you need to send. Once you have that, your project code only needs to define the buffer and write it to Serial1.

There is absolutely no reason why these tasks can not be done in the same block of code.

I hope you post a video of your button in action … I’d love to see it.

Anyone else having problems compiling for the particle?

Put the DFPlayer_mp3_mini.h & Softwareserial.h in the main file but still get an error.

any ideas would be appreciated!

There is no SoftwareSerial on Particle devices.
The substitute would be ParticleSoftSerial

Try this (which substitutes the libraries - you need to import ParticleSoftSerial)


#include "Arduino.h"
#include "ParticleSoftSerial.h"
#include "DFRobotDFPlayerMini.h"

ParticleSoftSerial mySoftwareSerial(D1, D0); // RX, TX

Code compiles with that example but DFPlayer still doesn’t work. I have it working on Arduino but no success on the particle.
wireing as follows:
VIN - > 5v
GND - > GND
TX - > 1k ->TX
RX - > RX

RX and TX need to be crossed RX ← TX and TX → RX

What one device T ransmits needs to be R eceived by the other and vice versa.

BTW, the sample I linked you to uses ParticleSoftSerial with D0 as the (software)TX and D1 as (software)RX.

2 Likes

Hi all!

Thanks for this great thread and suggestion…

I’ve made this:

Since I was there I also made Android app:

And Pebble app:

Good fun :slight_smile:

1 Like

Very nice application, well done!
:wave::older_man:

1 Like

What I’d like to do is like a voice/audible SCADA system. You press a button to trigger an audible playback of the status of a few sensors dotted around the building. So I’d need to use various IF statements combined with some way of figuring out when the mp3 player has finished playing a track and is ready for the command to play the next track. Anyone done anything similar? Thanks!

The MP3 module has a BUSY pin to tell whether it's currently playing or not.
The easies was to correlate input states with audio files would be via consecutevly numbered MP3 files and and enum in code.

Since the "demands" are anything but special I'd say almost everybody in this community has checked for a pin state and interpreted multiple sensors to decide on what to do next.
What do you hope to get?