Weird clicking from MP3 module when delay is called (or Millis)

Hi

Not specific to a current project, but I use JQ8400 serial connected MP3 modules occasionally (9600), and they are very prone to interference.

You need a decent power supply for starters.

But, I have noticed that if you call a delay (I know.... the devils work), it results in a loud click from the MP3 module when any MP3 command follows after the delay (even if other commands have been executed between the delay and the next MP3 command).

I don't usually use delays. This was to halt the startup routine briefly while other things got ready.

So I tried (going from memory here...)

unsigned long DelayMillis;

DelayMillis=millis(); do{}while(Millis-DelayMillis<3000);

But this still results in the click.

I also tried clearing the serial buffer with a clearance loop routine before calling the MP3 command, but it still did it.
I ended up binning the delay until after the MP3 had done it's startup sound effect.

What does the delay and/or millis() function do that might interfere with the MP3 module? Any idea?

Just found this snippet here from an old project... just for a bit of clarification

 //---------------- Set up MP3 ----------------------

Serial2.write(0xAA);Serial2.write(0x0B);Serial2.write(0x01);Serial2.write(0x02);Serial2.write(0xB8);             // Select Flash as the drive for sound files
delay(1000);
Serial2.write(0xAA);Serial2.write(0x04);Serial2.write(0x00);Serial2.write(0xAE);                                 // Stop all music files
delay(1000);
Checksum=190+SFX_volume;                                                                                         // Checksum=190+level                      
Serial2.write(0xAA);Serial2.write(0x13);Serial2.write(0x01);Serial2.write(SFX_volume);Serial2.write(Checksum);   // Set the volume (0-31)
Checksum=179+Graphic;
Serial2.write(0xAA);Serial2.write(0xA1);Serial2.write(0x01);Serial2.write(Graphic);Serial2.write(Checksum);      // Set the graphic equaliser

//-----------------------------------------------------

//Any kind of delay/millis() here causes the next command to produce a loud click

//-----------------------------------------------------

Checksum=181;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(2);Serial2.write(Checksum);           // Plays track 2 (Short beep)   Checksum is 179+the track number
delay(1000);
Checksum=182;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(3);Serial2.write(Checksum);           // Plays track 3 (Reload)  Checksum is 179+the track number
DelayMillis=millis(); do{}while(Millis-DelayMillis<3000);

This is exactly what delay() do, blocking until time has elapsed.

You don't want to block the execution of the rest of the program, so you need to use the method shown in example "Blink Without Delay"

Usually when I want to do things in sequence with a delay between, I use what is called a finite state machine

Here is an example

enum mp3FsmState : uint8_t
{
    STOPPED,
    DONE,
    WAIT,
    SELECT_FLASH,
    STOP_ALL_MUSIC,
    SET_VOLUME,
    SET_GRAPHEQ,
    PLAY_TRACK2,
    PLAY_TRACK3
};

mp3FsmState mp3FsmCurrentState = STOPPED;

void mp3FsmStart()
{
  if ( mp3FsmCurrentState == STOPPED )
  {
    Serial.println( "Starting MP3 FSM." );
    mp3FsmCurrentState = SELECT_FLASH;
  }
  else
  {
    Serial.println( "MP3 FSM already started." );
  }
}

void mp3FsmLoop()
{
  if ( mp3FsmCurrentState != STOPPED )
  {
    static uint32_t waitTime = 0;
    static mp3FsmState nextState = DONE;

    switch ( mp3FsmCurrentState )
    {
      case DONE :
      default :
      {
        Serial.println( "MP3 FSM finished!" );
        mp3FsmCurrentState = STOPPED;
        break;
      }

      case WAIT :
      {
        uint32_t currentMillis = millis();
        static uint32_t previousMillis = currentMillis;
        if ( currentMillis - previousMillis >= waitTime )
        {
          previousMillis = currentMillis;
          mp3FsmCurrentState = nextState;
        }
        break;
      }

      case SELECT_FLASH :
      {
        Serial.println( "Selecting Flash as the drive for sound files, and waiting 1000ms..." );

        // Select Flash as the drive for sound files
        Serial2.write(0xAA);
        Serial2.write(0x0B);
        Serial2.write(0x01);
        Serial2.write(0x02);
        Serial2.write(0xB8);

        waitTime = 1000;
        mp3FsmCurrentState = WAIT;
        nextState = STOP_ALL_MUSIC;
        break;
      }

      case STOP_ALL_MUSIC :
      {
        Serial.println( "Stopping all music, and waiting 500ms..." );

        // Stop all music files
        Serial2.write(0xAA);
        Serial2.write(0x04);
        Serial2.write(0x00);
        Serial2.write(0xAE);

        waitTime = 500;
        mp3FsmCurrentState = WAIT;
        nextState = SET_VOLUME;
        break;
      }

      case SET_VOLUME :
      {
        Serial.println( "Setting volume, and waiting 500ms..." );

        // Set the volume (0-31)
        Checksum=190+SFX_volume;
        Serial2.write(0xAA);
        Serial2.write(0x13);
        Serial2.write(0x01);
        Serial2.write(SFX_volume);
        Serial2.write(Checksum);

        waitTime = 500;
        mp3FsmCurrentState = WAIT;
        nextState = SET_GRAPHEQ;
        break;
      }

      case SET_GRAPHEQ :
      {
        Serial.println( "Setting graphic equalizer, and waiting 1000ms..." );

        // Set the graphic equaliser
        Checksum=179+Graphic;
        Serial2.write(0xAA);
        Serial2.write(0xA1);
        Serial2.write(0x01);
        Serial2.write(Graphic);
        Serial2.write(Checksum);

        waitTime = 1000;
        mp3FsmCurrentState = WAIT;
        nextState = PLAY_TRACK2;
        break;
      }

      case PLAY_TRACK2 :
      {
        Serial.println( "Playing track2, and waiting 1000ms..." );

        // Plays track 2 (Short beep)   Checksum is 179+the track number
        Checksum=181;
        Serial2.write(0xAA);
        Serial2.write(0x07);
        Serial2.write(0x02);
        Serial2.write(0x00);
        Serial2.write(2);
        Serial2.write(Checksum);

        waitTime = 1000;
        mp3FsmCurrentState = WAIT;
        nextState = PLAY_TRACK3;
        break;
      }

      case PLAY_TRACK3 :
      {
        Serial.println( "Playing track3, and waiting 2000ms..." );

        // Plays track 3 (Reload)  Checksum is 179+the track number
        Checksum=182;
        Serial2.write(0xAA);
        Serial2.write(0x07);
        Serial2.write(0x02);
        Serial2.write(0x00);
        Serial2.write(3);
        Serial2.write(Checksum);

        waitTime = 2000;
        mp3FsmCurrentState = WAIT;
        nextState = DONE;
        break;
      }
    }
  }
}

You call mp3FsmStart() when you want to start it, for example in setup(). And in loop() you call mp3FsmLoop() so that it runs continuously

I realise delays are bad. Didn't realise the Millis loop I made did the same thing however.

But... I do want to to the stop the execution of the rest of the program until I am ready.

Or, do you mean that delay is stopping the MP3 background process from continuing, and that is probably causing the click?

I am not going to lie... your code Guix baffles me. Seems SO much more complicated than my (clearly incorrect) code. Hobby level at best here.

Hmm I cannot cure this issue..

Tried using Millis(); as below (yes it's a mess.... but it was a quick testing routine).

It needed a flag to stop the command performing more than once.
The commented out code it how it used to be.

But, it now make an even worse click. The old code including the delay statements actually works slightly better.
Size of delay between statements makes no difference.

If you tell it to only play a single MP3 file and nothing else, it seems to be OK.

Clearly something is making the comms to the MP3 happy (it's on a Mega).

These commands (volume, select drive etc) obviously only need to be called once, and I cannot see how else to do it without stopping the routine.

Any ideas?

//------------------ MP3 Player ---------------------

byte Checksum;                                                                                       // MP3 player checksum value
byte SFX_volume=15;                                                                                  // Volume of any SFX
byte Graphic=0;                                                                                      // MP3 player graphic equaliser setting  0=Normal. 1=Pop.  2=Rock(hard). 3=Jazz.  4=Classic

unsigned long CurrentMillis;
unsigned long FunctionMillis;   

boolean SFX1;
boolean SFX2;
boolean SFX3;
//---------------------------------------------------


void setup() {

Serial2.begin(9600);                                                                                             // Serial2 comms to the MP3 module 17=RX 16=TX

//Serial2.write(0xAA);Serial2.write(0x0B);Serial2.write(0x01);Serial2.write(0x02);Serial2.write(0xB8);             // Select Flash as the drive for sound files
//delay(1000);
//Serial2.write(0xAA);Serial2.write(0x04);Serial2.write(0x00);Serial2.write(0xAE);                                 // Stop all music files
//delay(1000);
//Checksum=190+SFX_volume;                                                                                         // Checksum=190+level                      
//Serial2.write(0xAA);Serial2.write(0x13);Serial2.write(0x01);Serial2.write(SFX_volume);Serial2.write(Checksum);   // Set the volume (0-31)
//Checksum=179+Graphic;
//Serial2.write(0xAA);Serial2.write(0xA1);Serial2.write(0x01);Serial2.write(Graphic);Serial2.write(Checksum);      // Set the graphic equaliser



//Checksum=181;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(2);Serial2.write(Checksum);           // Plays track 2 (Short beep)   Checksum is 179+the track number//delay(1000);
 
//Checksum=182;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(3);Serial2.write(Checksum);           // Plays track 3 (Reload)  Checksum is 179+the track number

CurrentMillis=millis();
FunctionMillis=millis();
}

//---------------------------------------------------------------------------------

void loop() {

  
  CurrentMillis=millis();

  if ((CurrentMillis - FunctionMillis == 1500) and (SFX1==false)){SFX1=true;Checksum=190+SFX_volume;Serial2.write(0xAA);Serial2.write(0x13);Serial2.write(0x01);Serial2.write(SFX_volume);Serial2.write(Checksum);}     // Set volume

  if ((CurrentMillis - FunctionMillis == 2500) and (SFX2==false)){SFX2=true;Checksum=181;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(2);Serial2.write(Checksum);}     // Play SFX

  if ((CurrentMillis - FunctionMillis == 3500) and (SFX3==false)){SFX3=true;Checksum=182;Serial2.write(0xAA);Serial2.write(0x07);Serial2.write(0x02);Serial2.write(0x00);Serial2.write(3);Serial2.write(Checksum);}     // Play SFX
}

Could it pick up issues with the RX/TX lines? The MP3 module is mounted on a blank shield directly above the Mega, but the RX/TX lines are actually a couple of wires 3 inches long underneath the shield.

Phoneystark2020, if you would, try to put a 1K resistor on BOTH the TX and RX lines and see if that helps. I THINK you may be experiencing "speaker pop" that dogged a lot of MP3 players there for a while.

When you say a 1k on the lines.... you mean to pull them high?
I actually don't need the line from the MP3 to the Arduino, so that can go (not requesting any MP3 data, just sending commands).

Would enabling PULLUP on the serial pins achieve the same thing?

It's annoying, because it comes and goes. It wasn't there for a while yesterday.
I have disconnected all other sources of possible interference. and no joy.

One thing I did note, was that I ran the MP3 module off my power pack separately, and it appeared to fix it (the MP3 shares the Arduino's 5v onboard supply).

So I made a 7805 regulator board -0.1uf and 100uf on both the input and outputs and ran it from that instead..... noise came back.

It's now actually more of a 'thurrbth' than a click. Definitely some kind of interference on the lines.

I have noticed that if you use an external USB stick on these MP3 modules (they have Micro SD ports), then the power switching to the USB stick causes very bad speaker click.
The way around that was careful selection of a USB stick that appeared to have good power suppression and a short (4") USB extension lead with a ferrite coil on it.

I'm reasonably sure that it is the shield and not the code that causes the pops. Did you try without delays?

If you comment the code, it's easier for us to understand what it's doing; don't forget that not everybody is familiar with the shield and what the bytes that you send do.

Yes ,I am fairly sure this is a wiring thing... but these JQ8400 boards are madly touchy to noise.

Having said that... a delay statement will induce an odd 'blip' noise before the command is carried out.

I thought my code was commented? The serial statements are just the commands for this particular MP3 device.

That short routine basically ran, and operated each command one second apart only once. Using no delay statements.
Am I wrong there? Is the Millis(); based method I have used as bad as a delay statement?

I had to add the flag, as it appeared to operate the command more than once before the Millis statement went false, and that really does make the mess of the MP3 module.

OK.

Tried running MP3 on it's won supply.. Nope

Removed TX/RX lines from the shield (so only connections are now the supply). Ran short TX through a ferrite coil. Nope.

Tried 1k holdup. Nope

Kinda out of ideas now.

Going to try a different MP3 module

MP3 Module. Changed it for one out of another unit.... sound has vanished.
Very annoying waste of time!

3 hr later update...... it's back