Capturing unsolicited messages with the YX5300

Hello everyone. I’m a newb to the forums, but have a good amount of experience working with Arduinos. I am working with the YX5300 MP3 Player module and an Arduino Uno, and for the most part things are pretty good. After scouring around, I’ve been studying marco_c’s excellently designed library.

However, my question is about some of the other example code that I’ve seen out there. It’s tickling my brain and I can’t seem to figure it out. I’ve seen various versions of this code in several places online, and none of them have the capturing unsolicited messages working. This means you can’t query the MP3 player for volume, total tracks, status, etc…

#include <SoftwareSerial.h>

#define ARDUINO_RX 5  //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 6  //connect to RX of the module

SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);

static int8_t Send_buf[8] = {0}; // Buffer for Send commands.  // BETTER LOCALLY
static uint8_t ansbuf[10] = {0}; // Buffer for the answers.    // BETTER LOCALLY

String mp3Answer;           // Answer from the MP3.


String sanswer(void);
String sbyte2hex(uint8_t b);


/************ Command byte **************************/
/* Various CMD defines...
/************ Opitons **************************/

void setup()
{
  Serial.begin(9600);
  mp3.begin(9600);
  delay(500);

  sendCommand(CMD_SEL_DEV, 0, DEV_TF);
  delay(500);
  
}


void loop()
{
  char c = ' ';
  // If there a char on Serial call sendMP3Command to sendCommand
  if ( Serial.available() )
  {
    c = Serial.read();
    sendMP3Command(c);
  }

  // Check for the answer.
  if (mp3.available())
  {
    Serial.println(decodeMP3Answer());
  }
  delay(100);
}

void sendMP3Command(char c) {
  
  switch (c) {
    case '?':
    case 'h':
      Serial.println("HELP  ");
      Serial.println(" p = Play");
      Serial.println(" P = Pause");
      Serial.println(" > = Next");
      Serial.println(" < = Previous");
      Serial.println(" s = Stop Play"); 
      Serial.println(" + = Volume UP");
      Serial.println(" - = Volume DOWN");
      Serial.println(" c = Query current file");
      Serial.println(" q = Query status");
      Serial.println(" v = Query volume");
      Serial.println(" x = Query folder count");
      Serial.println(" t = Query total file count");
      Serial.println(" f = Play folder 1.");
      Serial.println(" S = Sleep");
      Serial.println(" W = Wake up");
      Serial.println(" r = Reset");
      break;


    case 'p':
      Serial.println("Play ");
      sendCommand(CMD_PLAY);
      break;

    case 'P':
      Serial.println("Pause");
      sendCommand(CMD_PAUSE);
      break;

    case '>':
      Serial.println("Next");
      sendCommand(CMD_NEXT_SONG);
      sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
      break;

    case '<':
      Serial.println("Previous");
      sendCommand(CMD_PREV_SONG);
      sendCommand(CMD_PLAYING_N); // ask for the number of file is playing
      break;

    case 's':
      Serial.println("Stop Play");
      sendCommand(CMD_STOP_PLAY);
      break;


    case '+':
      Serial.println("Volume Up");
      sendCommand(CMD_VOLUME_UP);
      break;

    case '-':
      Serial.println("Volume Down");
      sendCommand(CMD_VOLUME_DOWN);
      break;

    case 'c':
      Serial.println("Query current file");
      sendCommand(CMD_PLAYING_N);
      break;

    case 'q':
      Serial.println("Query status");
      sendCommand(CMD_QUERY_STATUS);
      break;

    case 'v':
      Serial.println("Query volume");
      sendCommand(CMD_QUERY_VOLUME);
      break;

    case 'x':
      Serial.println("Query folder count");
      sendCommand(CMD_QUERY_FLDR_COUNT);
      break;

    case 't':
      Serial.println("Query total file count");
      sendCommand(CMD_QUERY_TOT_TRACKS);
      break;

    case 'f':
      Serial.println("Playing folder 1");
      sendCommand(CMD_FOLDER_CYCLE, 1, 0);
      break;

    case 'S':
      Serial.println("Sleep");
      sendCommand(CMD_SLEEP_MODE);
      break;

    case 'W':
      Serial.println("Wake up");
      sendCommand(CMD_WAKE_UP);
      break;

    case 'r':
      Serial.println("Reset");
      sendCommand(CMD_RESET);
      break;
  }
}


String decodeMP3Answer() {
  String decodedMP3Answer = "";

  decodedMP3Answer += sanswer();

  switch (ansbuf[3]) {
    case 0x3A:
      decodedMP3Answer += " -> Memory card inserted.";
      break;

    case 0x3D:
      decodedMP3Answer += " -> Completed play num " + String(ansbuf[6], DEC);
      break;

    case 0x40:
      decodedMP3Answer += " -> Error";
      break;

    case 0x41:
      decodedMP3Answer += " -> Data recived correctly. ";
      break;

    case 0x42:
      decodedMP3Answer += " -> Status playing: " + String(ansbuf[6], DEC);
      break;

    case 0x48:
      decodedMP3Answer += " -> File count: " + String(ansbuf[6], DEC);
      break;

    case 0x4C:
      decodedMP3Answer += " -> Playing: " + String(ansbuf[6], DEC);
      break;

    case 0x4E:
      decodedMP3Answer += " -> Folder file count: " + String(ansbuf[6], DEC);
      break;

    case 0x4F:
      decodedMP3Answer += " -> Folder count: " + String(ansbuf[6], DEC);
      break;
  }

  return decodedMP3Answer;
}


void sendCommand(byte command){
  sendCommand(command, 0, 0);
}

void sendCommand(byte command, byte dat1, byte dat2){
  delay(20);
  Send_buf[0] = 0x7E;    //
  Send_buf[1] = 0xFF;    //
  Send_buf[2] = 0x06;    // Len
  Send_buf[3] = command; //
  Send_buf[4] = 0x01;    // 0x00 NO, 0x01 feedback
  Send_buf[5] = dat1;    // datah
  Send_buf[6] = dat2;    // datal
  Send_buf[7] = 0xEF;    //
  Serial.print("Sending: ");
  for (uint8_t i = 0; i < 8; i++)
  {
    mp3.write(Send_buf[i]) ;
    Serial.print(sbyte2hex(Send_buf[i]));
  }
  Serial.println();
}

String sbyte2hex(uint8_t b)
{
  String shex;

  shex = "0X";

  if (b < 16) shex += "0";
  shex += String(b, HEX);
  shex += " ";
  return shex;
}

int shex2int(char *s, int n){
  int r = 0;
  for (int i=0; i<n; i++){
     if(s[i]>='0' && s[i]<='9'){
      r *= 16; 
      r +=s[i]-'0';
     }else if(s[i]>='A' && s[i]<='F'){
      r *= 16;
      r += (s[i] - 'A') + 10;
     }
  }
  return r;
}

String sanswer(void)
{
  uint8_t i = 0;
  String mp3answer = "";

  // Get only 10 Bytes
  while (mp3.available() && (i < 10))
  {
    uint8_t b = mp3.read();
    ansbuf[i] = b;
    i++;

    mp3answer += sbyte2hex(b);
  }

  // if the answer format is correct.
  if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
  {
    return mp3answer;
  }

  return "???: " + mp3answer;
}

I’ve been wracking my brain trying to figure out why it isn’t picking up unsolicited messages. I’ve studied the message flow article from here, but I guess I wanted to understand why these examples don’t for my own sanity. I’ve tested the module against marco_c’s library and I can see that it does handle unsolicited messages correctly. Whenever I try to send any commands requesting information from the above code, I simply get the ‘ACK’ response and never get the message with the requested data.
Thanks for the help!

  if (mp3.available())
  {
    Serial.println(decodeMP3Answer());
  }

I think this is your problem. mp3.available() simply says that at least one character has arrived, it is not necessarily the entire message. Because your loop() is going to execute many times before the whole message arrives, you need to buffer up the message characters until you see the end of message character (can't remember what that is exactly). Here is the relevant code from MD_YX5300 check() routine:

  // process all the characters waiting
  do
  {
    c = _Out.read();
 
    if (c == PKT_SOM) _bufIdx = 0;      // start of message - reset the index
    
    _bufRx[_bufIdx++] = c;

    if (_bufIdx >= ARRAY_SIZE(_bufRx))  // keep index within array memory bounds
      _bufIdx = ARRAY_SIZE(_bufRx) - 1;
  } while (_Out.available() && c != PKT_EOM);

  // check if we have a whole message to 
  // process and do something with it here!
  if (c == PKT_EOM)
  {
    processResponse();
  }

check() will keep buffering the current message until the end is detected. You need to do something similar.

Hi marco_c! Thanks so much for replying! Also thank you for all the work you put in to your library and sharing it. I hope to better my design in embedded devices, and you’ve left a great case study. :slight_smile:

So I’ve tried to directly copy the serial buffering code from your library, but I’m still only getting the Ack reply from my original query (Asking for the Total File Count), and nothing else. It checks for mp3.available again, but nada. I never receive that unsolicited message that I’m expecting.

String sanswer(void)
{
  uint8_t i = 0;
  String mp3answer = "";
  uint8_t b;

  do
  {
    b = mp3.read();

    // Reset index if we see the start packet
    if(b == 0x7E) 
      i = 0;

    ansbuf[i++] = b;    

    // keep index within array memory bounds
    if (i >= ARRAY_SIZE(ansbuf))  
      i = ARRAY_SIZE(ansbuf) - 1;

    mp3answer += sbyte2hex(b);

  } while (mp3.available() && b != 0xEF);
        
  // If we've seen end packet
  if (b == 0xEF)
  {
   if(mp3.available())
    Serial.println(F("More data available"));
    
    return mp3answer;
  }

  return "???: " + mp3answer;
}

I’ve also changed that ‘if’ in loop() to a ‘while’ to keep us in a state where we’re constantly checking for messages, but still that requested info never comes through.

// Check for the answer.
  while (mp3.available())
  {
    Serial.println(decodeMP3Answer());
  }
  delay(100);
}

One thing I did find out however, was that if I turn off the feedback when I’m sending requests…

void sendCommand(byte command, byte dat1, byte dat2){
  delay(20);
  Send_buf[0] = 0x7E;    //
  Send_buf[1] = 0xFF;    //
  Send_buf[2] = 0x06;    // Len
  Send_buf[3] = command; //
  Send_buf[4] = 0x00; <------   // 0x00 NO, 0x01 feedback
  Send_buf[5] = dat1;    // datah
  Send_buf[6] = dat2;    // datal
  Send_buf[7] = 0xEF;    //
  Serial.print("Sending: ");
  for (uint8_t i = 0; i < 8; i++)
  {
    mp3.write(Send_buf[i]) ;
    Serial.print(sbyte2hex(Send_buf[i]));
  }
  Serial.println();
}

I finally get the requested informationbut of course no Ack messages from my request. It’s interesting…

Any ideas on why it would work when I disable the acknowledge responses?

I think what could be is happening is that you are processing the ACK to your response and then not the unsolicited message.

When you send the request for the file count, that request gets and ack. Following that ack, up to 1 second later (but usually much faster) you will get an unsolicited message with the response. The exchange is composed of 4 message:

  1. Your initial request
  2. ACK response
  3. Unsolicited response with data
  4. Your ACK response to the unsolicited message

When you turn off the ack in the request packet you are skipping message 2 and then processing the next message (the file count).

I don't see all your code, so I can't tell if it is anything else. For example, in your original code you called sanswer ()and the printed the results - you should only do this is sanswer() has actually got a complete answer, as it is buffering the response. Simple way is to return a boolean true if the answer is available.