Created a library for the MDFLY MP3 module (TDB380)

I created a library for use with the MDFLY MP3 module. It is apparently the same as a Tenga TDB380 module, but there are some differences between the commands. My library works on any pin, so it doesn't take up the serial port.

On my MDFLY module, retrieving the number of files in a folder works, but in the Tenga datasheet it says these commands are no longer valid. My library has no receive method, so either way you can't get the result. I did it that way because it was much simpler and smaller code and since those types of commands are supposed to be invalid anyway...

Constructor:

serMP3 mp3(10,11) //TX pin, Busy Pin

Methods are:

begin(00-31) //intializes the module and sets the initial volume
play(001 -199)
pause()
stop()
playRand()
volUP()
volDN()
setVol(00-31)
cd(01-15) //Change directory
send(byte) //I made this function public so that you can send other commands if they happen to be supported by your module)
checkBsy() //gets the status of the Busy pin

Here is my example sketch:

#include <serMP3.h>

//union serial_data {
//   unsigned long number;
//   byte read_byte[3];
//} data;



serMP3 MP3(11,10);


void setup(){
  
  MP3.begin(31);
  Serial.begin(9600);
  
}

void loop(){
  byte sc = 0;
  unsigned long number = 0;
  byte n =0;
  
  if(Serial.available()){
    sc = Serial.read();   
  }
  
  if(sc == 'p' || sc == 'P'){ //Play (001-199)
    number=Serial.parseInt();
    n = byte(number);
    MP3.play(n);
  }
  if(sc == 's' || sc == 'S'){ //Stop Playing
    MP3.stop();
  }
  if(sc == '+'){ //Voume Up
    MP3.volUP();
  }
  if(sc == '-'){ //Volume Down
    MP3.volDN();
  }
  if(sc == 'v' || sc == 'V'){ //Set Volume (01-31)
    number=Serial.parseInt();
    n = byte(number);
    MP3.setVol(n);
  }
  if(sc == 'f' || sc == 'F'){ //Change Directory (01-15) 01 is root
    number=Serial.parseInt();
    n = byte(number);
    MP3.cd(n);
  }
  if(sc == '*'){ //Play a random file (will switch back to the root folder! Only plays from root folder)
   MP3.playRand();
  }
  if(sc == '/'){ //Pause/Resume
   MP3.pause();
  }
  
  
  
}

Hopefully someone finds it useful. Parts of this were taken from the softwareSerial library, but heavily modified and will not interfere with it.

serMP3.cpp (2.46 KB)

serMP3.h (1.23 KB)

Thanks for sharing your library! Is this the module you are talking about? http://www.mdfly.com/index.php?main_page=product_info&products_id=284

I looks good. What hardware are you using with it?

cyclegadget:
Thanks for sharing your library! Is this the module you are talking about? http://www.mdfly.com/index.php?main_page=product_info&products_id=284

I looks good. What hardware are you using with it?

That is the correct one. I am using it with the Uno Rev. 3.

Firstly,many thanks for taking the trouble to post your library. It appears to be exactly what I need.
I am currently building a talking scarecrow and have purchased the tdb380.
Unfortunately my understanding of the arduino is still very rudimentary.
I have wired the thing up using pins 10 and 11 and have created a directory in the library called serMP3 and copied your two files into it.
I have called up the library and pasted up your example program. It compiles and uploads
OK and there is a brief snatch of sound from one of the several files I have added to the sd card.
So, hopefully quite a lot is OK ! What I can't figure out although I'm sure that its embarrassingly
obvious is how to call up a specific track. Do I insert a command int the sketch loop ?

If you mean from my example program, you would enter P001 to play MP3 number one, P002 to play MP3 number 2 and so on. This is using the serial terminal.

The TDB380 is 'stupid'. It doesn't matter what you name the MP3s. It calls the track by what order it is in the FAT table. So, you have to be careful how you copy them over and make sure they are copied in the order you want to address them. There is a program that can re-arrange stuff called DriveSort (Anerty's Lair - DriveSort)

If you just mean from your own code, you first have to create an instance (from my example):

serMP3 MP3(11,10);

The new instace is named MP3, but you can name that anything. 11 and 10 are the pin numbers. 11 is Tx and 10 is the busy signal.

Then to play something you call it like this:

MP3.play(n);

Where n is the number of the track you want to play.There are several other functions controlling all aspects of the module. See my original post to see how to use them. But it will always be called like MP3.stop() for example, to stop playback.

Send pause to pause the playback, and send pause again to resume.

Thanks ! I think I understand. will go away and do some fiddling !

Just let me know if you have any questions or find any errors. Also appreciate any suggestions once you get things up and running.

Sorry about this. I am now getting no sound at all. Pins 11 and 11 are connected to 15 and 13 on the module, the amplifier is OK and connected to pin18. When I select the serial monitor and enter p001 then send... Nothing happens. I have a strong feeling that I am doing something daft. I haven't tried writing my own sketch yet as I don't really understand what you mean by creating my own instance, but I'm busy consulting books to try and find out !
Cheers

Do you happen to have other files on the SD card (non-Mp3s, etc...)?

No.. Just the system volume info (0 bytes) and 6 short mp3 files. I did get them going after a fashion using the sketch in instructables using pins 1 and 2 . It worked but I never quite got the file order sorted although I did use the program you mentioned to sort them.

So it worked using the real serial port but you couldn't get it to work using pin 11? That was the only difference? Are you connecting GND on the module to the Arduino ground?

Thanks for sharing,

Some minor remarks on the lib

void serMP3::begin(uint8_t vol)
{
	delay(3);  // is this really needed??????
	setVol(vol);
	delay(5);
	stop();
}
bool serMP3::busy()    // just busy 'reads/sounds' better in an if or while statement if (!busy()) or while(busy())
{
	return (digitalRead(_bsyPin) == HIGH);
}

further you could consider making small functions inline (directly in the .h file) to reduce the footprint of sketches.
(send must be public ?)

	inline void stop() { send(239) };
	inline void pause() { send(234) };
	inline void volUP() { send(232) };
	inline void volDN() { send(233) };

Great suggestions. The delay was needed at least on mine. It takes a moment for the module to respond. Before I added it, the volume was not getting changed. Someone may experiment with removing it and see if works for them.

I will make the other changes that you suggested. Especially the inline functions. I hadn't known how to do that before. Very useful.

Yep, Gnd was connected to module and arduino. Think its time to go back to the real serial and see if it still works with that or if I've done something really daft !

Tried using pin 1 to tx and 2 as busy with the following sketch to test the module
[const int busyPin = 2; // the number of the Busy pin

void setup() {
// initialize the serial communication:
Serial.begin(4800); //Set to 4800 bps
Serial.write(0xEF); // Reset board
delay(2000);
pinMode(busyPin, INPUT);
}

void loop(){
if (digitalRead(busyPin) ==LOW) {
//device busy
}
else {
Serial.write(0); // Play Random file
delay(5000);
}
}]
The result was to play one of the files at full volume. when I recompile and run it plays the same file at greatly reduced volume and if I change the file number it plays the same file again at reduced volume. Whilst I don't expect people to spend too much time on my problem If there is an obvious error here I would like to know what it is !

Nearly there ! The main problem appears to be the volume. The data sheet seems to be wrong. The +volume pin is shown as pin 9 when it seem to be grounding pin 10 that increases the volume. I now have to sort out how to get the files in the right order and then how to call them from within my sketch rather than via the serial monitor ! still at least we seem to be winning. Thanks for your patience and help.

As for the volume, I found that the module does not start up at full volume. That is why I put the volume setting in my .begin method so that you can set it at intialization. If you are looking at the MDFly datasheet, it is missing some of the commands.

You set the volume by writing C8-E7 (200-231). 231 being full volume. So in your setup() put Serial.write(231) to start up with full volume.

EF is not technically a reset. It is STOP. I put that in my code because the module sometimes starts up randomly playing an MP3. Using 0 to play a random file was silly in my opinion.

So, try this:

void setup(){
   Serial.begin(9600);
   Serial.write(0xEF); //Stop playing
   Serial.write(0xE0); //Set the volume just below full volume
}

It seems like your module is either just playing a random file at startup and your loop isn't working because the busy signal is active, or it only sees one file on the card for some reason.

I don't see anything wrong with your code (assuming you are changing the number from 0 for the file you want to play). BTW, you could snag the serial parsing part of my example code and just replace the calls with Serial.write commands if you wanted to try out all the features.

void loop(){
  byte sc = 0;
  unsigned long number = 0;
  byte n =0;
  
  if(Serial.available()){
    sc = Serial.read();   
  }
  
  if(sc == 'p' || sc == 'P'){ //Play (001-199)
    number=Serial.parseInt();
    n = byte(number);
    if(n > 0 && n < 200){
     MP3.play(n);
    }

    else {
      Serial.println("Error: Use a value 1 - 199);
    }
  }

  if(sc == 's' || sc == 'S'){ //Stop Playing
    Serial.write(0xEF);
  }

  if(sc == '+'){ //Voume Up
    Serial.write(0xE8);
  }

  if(sc == '-'){ //Volume Down
    Serial.write(0xE9);
  }

  if(sc == 'v' || sc == 'V'){ //Set Volume (01-31)
    number=Serial.parseInt();
    n = byte(number);
    n = n + 200;
    if(n >= 0 && n < 32){
    Serial.write(n);
    }
    else {
      Serial.println("Error: Use a value 0 - 31);
    }
  }

  if(sc == 'f' || sc == 'F'){ //Change Directory (01-15) 01 is root
    number=Serial.parseInt();
    n = byte(number);
    n = n + 240;
    if(n > 240 && n < 256){
    Serial.write(n);
    }
    else{
      Serial.println("Error: Use a value 1 - 15);
    }
  }

  if(sc == '*'){ //Play a random file (will switch back to the root folder! Only plays from root folder)
   Serial.write(0x00);
  }

  if(sc == '/'){ //Pause/Resume
   Serial.write(0xEB);
  }
}

No promises that there isn't a typo in there.

If it is working with the serial port, it should be working with my library. The only thing my library is doing mainly is emulating the Tx portion of a standard serial port at 4800 baud. I used code directly from the SoftwareSerial library to do that part.

Everything else is just formatting numbers to make it easier to understand, and doing some error checking.

Nothing wild going on with it.

Everything seems to be working ok now. (My grumpy scarecrow is grumbling away nicely now !) Only two things puzzle me.
Using the code below I am surprised that after the function Playfile is executed that it does not return to the main loop and then repeat.
Also when I upload the program it initially plays the file (n)which was set previously. Only when I run for the second time does it use the new value of (n).
Any comments would be appreciated.

You forgot to post your code.