Unsolvable DFPlayer Mini (clone) communication issue with ESP32-S3 (Works with Arduino Uno)

Hi everyone, I'm really stuck on a baffling issue involving an ESP32-S3 and a DFPlayer Mini module. After days of debugging, nothing makes sense anymore—maybe someone here has run into something similar?

The Goal:
A simple project: play an MP3 file from a DFPlayer Mini using an ESP32-S3 (with a display and keypad later).

The Problem:

  • When connected to the ESP32-S3, the DFPlayer Mini is either completely silent or, under certain wiring configurations, outputs a loud, continuous digital noise (like a "tractor" sound) from the speaker. The ESP32 code reports that the DFPlayer is not responding.
  • When the exact same DFPlayer Mini, SD card, and speaker are connected to an Arduino Uno, everything works perfectly.

So the DFPlayer module, SD card, MP3 file, and speaker are all fine. The issue is clearly with the ESP32-S3 setup.


Hardware Used:

  • Microcontroller: ESP32-S3 development board
  • DFPlayer Module: MH2024K-24SS based (PCB marked MP3-TF-16P V3.0).
  • SD Card: Known-good card, FAT32 formatted using the official SD Card Formatter. Contains a /mp3 folder with 0001.mp3 (128kbps CBR).
  • Power Supply: Stable 5V 1A wall adapter powering the ESP32 via USB.
  • Logic Level Shifter: 4-channel bidirectional (BSS138-based)

What I’ve Tried (Summary):

Using the Arduino Uno, everything works without issues. A simple setup with a resistor on the RX line is enough to get the DFPlayer playing audio immediately.

With the ESP32-S3, nothing works. I’ve tested both direct connections (with and without a resistor) and setups using a proper bidirectional logic level shifter. In one case, I get loud digital noise (even with the SD card removed); in the other, the module stays silent and player.begin() always fails. The level shifter itself works fine—I verified it with a loopback test.

I’ve tried multiple UARTs (Serial1, Serial2, main UART) and different pins, but no luck. The main UART even interferes with uploads when the DFPlayer is connected.

I also cycled through all common baud rates, including 9600, 4800, and 19200—none of them worked.


Minimal Test Code for ESP32 with Logic Level Shifter (Fails):

#include "DFRobotDFPlayerMini.h"
#include "HardwareSerial.h"

// Using Hardware Serial 2 on ESP32
HardwareSerial mySerial(2);
DFRobotDFPlayerMini player;

void setup() {
  // Init serial for debugging
  Serial.begin(115200);
  while (!Serial);
  Serial.println("\n--- Final Test: ESP32 with Logic Level Shifter ---");

  // Init serial for DFPlayer
  // ESP32 RX = Pin 19, ESP32 TX = Pin 21
  mySerial.begin(9600, SERIAL_8N1, 19, 21);
  Serial.println("DFPlayer serial port initialized.");

  Serial.println("Waiting 3 seconds for DFPlayer to boot...");
  delay(3000);

  Serial.println("Attempting to initialize DFPlayer...");
  if (player.begin(mySerial)) {
    Serial.println("**************************************");
    Serial.println("* SUCCESS! DFPlayer is online!       *");
    Serial.println("**************************************");
    player.volume(25);
    player.play(1);
    Serial.println("Play command sent.");
  } else {
    Serial.println("**************************************");
    Serial.println("* FAILURE! DFPlayer is not responding. *");
    Serial.println("**************************************");
  }
}

void loop() {}

Conclusions (and Desperation)

At this point, it looks like there’s some low-level incompatibility between this ESP32-S3 board and DFPlayer modules based on the MH2024K chip. I’m guessing some mix of electrical noise, UART timing, or something deeper in the hardware layer.

I’ve tried everything I can think of.
If anyone knows of issues with the ESP32-S3 UART or has suggestions I'd be very grateful.

Thanks in advance

Have you checked the Migration Document to see if that is tripping you up? If it is, then just roll back the Espressif (NOT Arduino) esp32 board to 2.0.17.

I see a lot of UART changes.

Thank you for your reply. I'm currently using version 2.0.14 because of an incompatibility with the ILI9488—it's the only version that works

Post an annotated Schematic, showing exactly how you have wired this. Be sure to include all components, power sources etc. A clear picture would be nice as well.

The ESP32-S3 uses 3.3V level signals, yes? So why would you feel the need to use a level shifter to communicate with the DFplayer, which also uses 3.3V level signals?

My ugly schematic

And a fast fritzing copy

To be honest, I don't know — it was just an attempt, because it worked with Arduino (at 5V, I suppose), so I gave it a try. Yes, it does use 3.3V though. Anyway, even without the level shifter, it still doesn't work

Use DFPlayerMini in I/O MODE. Connect SPK1, SPK2, VCC, GND. Use ESP DIO pins configured with INPUT_PULLUP to momentarily ground DFPlayerMin pins IO_1 or IO_2 for "next file", "prev file". Longer grounding of IO_1 or IO_2 will increase/decrease volume.

Can you please explain better? I can’t understand very well how to use in I/O mode. And this

I/O mode only needs a momentary grounding to change audio files and the volume. Connect your ESP VCC to DFPlayerMini VCC, GND to GND, connect one ESP pin to DFPlayerMini IO_2, connect another ESP pin to DFPlayerMini IO_1 (both configured with INPUT_PULLUP to hold the pins HIGH when not in use). When you want to "play" next file, momentarily send a LOW to the ESP pin connected to IO_1 or IO_2.

This image has a bunch of buttons, but you will use the ESP as your buttons.

In your void setup() function, do something like this...

  pinMode(ESP_DIO_PIN_ONE, INPUT_PULLUP);
  pinMode(ESP_DIO_PIN_TWO, INPUT_PULLUP);

Got it, thank you. So this would be useful mainly for testing, right?

In my case, I eventually need to use a keypad (not connected yet, since I’m testing components individually) to select specific tracks, so I’ll probably need full serial control.

Testing the DFPlayerMini, yes.

Code can be used to to "next/prev" to the track you want (and vol+/vol-).

I am retracting this... I wired the two Nano pins to the two DFPlayerMini pins, but I could not make the "next/prev" recognize a digitalWrite(pin, LOW); unless I loaded the pin down with an LED or messed with the jumper. Until I find why this is not working this time, I am retracting this.

Here is the code I was using...

void setup() {
  Serial.begin(115200);
  pinMode(3, INPUT_PULLUP);
}

void loop() {
  digitalWrite(3, LOW);
  delay(100);
  Serial.print(digitalRead(3));
  delay(2000);
  digitalWrite(3, HIGH);
  delay(100);
  Serial.print(digitalRead(3));
  delay(2000);
}

The results should be

0101010101...

but the results are

1111111111...

... unless I wiggle the jumper or add an LED to pin 3

Thanks for the follow-up! IO_1/IO_2 it's probably not a good fit for my use case anyway.
I need full control over track selection (eventually via a keypad), so I’ll need to get UART working properly on the ESP32-S3.
Still, I appreciate the suggestion.

Out of curiosity, do you have any other ideas on what could be causing this? I’ve seen many tutorials where the DFPlayer works just fine with an ESP32 (even without a level shifter), but for some reason I can’t get it to work with the S3. It’s really puzzling.

the following code works on a ESP32-S3-DevKitC-1

// ESP32-S3-DevKitC-1 DFRobotDFPlayer player

// **** Once the card is formatted, MP3 files can be copied to it. 
// **** They are played in the order in which they were copied to the card
// names make no difference

#include "DFRobotDFPlayerMini.h"

// ESP32-S3  connections
// ESP32-S3 3.3V to DFPlayer VCC
// ESP32-S3 GND to DFPlayer GND
// ESP32-S3 pin RX to DFPlayer TX
// ESP32-S3 pin TX to DFPlayer RX
// ESP32-S3 pin 4 to DFPlayer BUSY

#define RXD1 18
#define TXD1 17

#define mySoftwareSerial Serial1
#define BUSY 4

DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

void setup() {
  Serial.begin(115200);
  delay(1000);
  pinMode(4, INPUT_PULLUP);       // GPIO 4 is Busy signal
  Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);
  delay(3000);
  Serial.println();
  Serial.println(F("\nESP32-S3 DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true)
      ;
  }
  Serial.println(F("DFPlayer Mini online."));
  myDFPlayer.volume(25);  //Set volume value. From 0 to 30
  //myDFPlayer.play(1);     //Play the first mp3
  delay(10);
  Serial.println("enter track to play (1, 2, 3, etc)");
}

void loop() {
  static unsigned long timer = millis();
  static int busy = -1;
  // check if player Busy status has changed
  if (digitalRead(BUSY) != busy) {
    if ((busy = digitalRead(BUSY)))
      Serial.println("busy " + String(busy) + " NOT playing");
    else
      Serial.println("busy " + String(busy) + " playing");
    ;
  }

  if (millis() - timer > 3000) {
    timer = millis();
    //myDFPlayer.next();  //Play next mp3 every 3 second.
  }
  // if new track (integer) entered play it
  if (Serial.available()) {
    int x = Serial.parseInt();                // read track number
    Serial.println("playing " + String(x));
    myDFPlayer.play(x);                       // play track
    while (Serial.available()) Serial.read(); // clear EOL etc
    delay(10);
  }

  // if player status changed display
  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read());  //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value) {
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

serial monitor output as 1 2 3 etc is entered to select track to play

ESP32-S3 DFRobot DFPlayer Mini Demo
Initializing DFPlayer ... (May take 3~5 seconds)
DFPlayer Mini online.
enter track to play (1, 2, 3, etc)
busy 1 NOT playing
playing 1
busy 0 playing
Number:1 Play Finished!
busy 1 NOT playing
playing 2
busy 0 playing
Number:2 Play Finished!
busy 1 NOT playing
playing 3
busy 0 playing
Number:3 Play Finished!
busy 1 NOT playing
playing 4
busy 0 playing

Ok, it’s black magic but it works! So, it was probably the 5V causing the issue, because now that I’m using 3.3V it works fine (just some occasional noise). I tried switching back to 5V and yep, that’s definitely the problem. Thanks a lot, my friend!

I didn’t quite understand your diagram.
But can we agree that the +5V pin on the ESP32 is an input, not an output?

Also, is the BSS138 fast enough to keep up with a UART?

In general:
The input of the MP3 module can work well with the output levels of the ESP32 because it's at 3.2V, but not the other way around.
The MP3 module has outputs at 5V, so you should use a voltage divider between the TX of the MP3 module and the RX of the ESP32 — not the other way around.

With two resistors — one 1 kΩ and one 2 kΩ (or two 1 kΩ resistors for a total of 3 kΩ) — you should be able to level the voltage down from 5 V to 3.2 V.

If you're powering with two different sources, such as two power supplies, it's best to connect the two grounds together to have the same voltage reference with respect to ground.