esp32 esptool arduino platformio

"A fatal error occurred: Failed to connect to ESP32" — Every Cause I've Hit

Fix esptool's "Failed to connect to ESP32": charge-only cables, missing drivers, ports held by another program, boot-mode pins, the EN capacitor fix and Linux brltty.

· Mohammed Aquib Ansari

Every ESP32 project I’ve started has hit this at least once:

A fatal error occurred: Failed to connect to ESP32: No serial data received.

Sometimes it’s a different message, like Wrong boot mode detected (0x13)! The chip needs to be in download mode. or Timed out waiting for packet header. Whichever one you get, esptool opened a serial port, tried to talk to the ROM bootloader, and got nothing useful back.

There’s a short list of reasons, and the fastest fix is to work through them in order: physical layer first, then boot mode, then the operating system.

First, read which message you actually got

esptool’s wording tells you how far it got:

  • “Could not open port” / “Access is denied” / “Permission denied” / “Device or resource busy”: it never reached the chip. Something on your computer is blocking the port, so skip to the port section.
  • “No serial data received”: the port opened, but nothing came back at all. That points to the cable, the power, or a chip that isn’t running.
  • “Wrong boot mode detected (0x13)”: the chip answered, but it booted your firmware instead of the download ROM. 0x13 is the normal boot-from-flash mode, which means the auto-reset didn’t put it into download mode.
  • “Timed out waiting for packet header”: the chip answered, but the conversation broke down, often partway through. Think cable quality, baud rate, or power.

1. The cable is charge-only

I lost an evening to this once. A lot of USB cables, especially the ones bundled with power banks and cheap gadgets, have only the power wires. The board lights up, and the computer sees nothing.

A quick test: if no new port appears when you plug the board in (Device Manager on Windows, ls /dev/tty* before and after on Linux or macOS), swap the cable before trying anything else.

2. The USB-serial driver is missing

Most dev boards use a separate USB-to-serial chip, and you can usually read its part number on the board:

  • CP2102 / CP2104 (Silicon Labs): needs the CP210x driver on older Windows and some macOS versions.
  • CH340 / CH9102 (WCH): needs WCH’s driver on Windows and older macOS.
  • ESP32-S2, S3, C3 and C6 boards with no separate chip: these use the chip’s native USB and need no driver on current systems.

If the board shows up as an unknown device, or as nothing at all with a known-good cable, it’s the driver.

3. The port is already taken

Only one program can have a serial port open at a time. The usual culprits are a serial monitor still open in the Arduino IDE or PlatformIO, a second IDE window, a screen or minicom session, or another browser tab using Web Serial.

On Linux there are two classic background thieves:

  • brltty (a Braille display driver shipped with Ubuntu) claims CH340 adapters, so the port appears and then vanishes a second later. Unless you use a Braille display, sudo apt remove brltty fixes it.
  • ModemManager probes new ttyACM devices for a few seconds after they appear, thinking they might be modems. Wait a moment before flashing, or stop the service.

Also on Linux, “Permission denied” on /dev/ttyUSB0 means your user isn’t in the dialout group (uucp on Arch). Run sudo usermod -aG dialout $USER, then log out and back in.

4. The board didn’t enter download mode

To accept firmware, the chip has to come out of reset with its boot strapping pin held low:

  • ESP32, S2 and S3: GPIO0.
  • ESP32-C3 and C6: GPIO9.

Dev boards do this automatically. Two transistors let the USB chip’s DTR and RTS lines drive EN (reset) and the boot pin. When that circuit doesn’t do its job, do it by hand:

  1. Hold BOOT.
  2. Press and release EN (or RST).
  3. Release BOOT.
  4. Start the upload.

If you watch the serial output at 115200 while doing this, a classic ESP32 prints something like:

rst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download

waiting for download means it’s ready. If you see boot:0x13 (SPI_FAST_FLASH_BOOT) instead, it booted normally, and the boot pin wasn’t low at reset.

When the manual dance works but auto-reset doesn’t

Some cheap ESP32 DevKit clones release EN too quickly for the timing esptool uses. The fix that has worked for me on several of them is a 1–10 µF capacitor between EN and GND, which slows the reset line down just enough. It’s a two-minute solder job, and afterwards uploads work without touching the buttons.

Something on the strapping pins

If even the manual sequence fails, look at what’s wired to the strapping pins. A sensor or LED pulling GPIO0 (or GPIO9 on the C3/C6) the wrong way at reset, or GPIO2 pulled high on a classic ESP32, can stop the chip entering download mode. Disconnect the external circuit and try again.

5. Native USB boards are a special case

On an ESP32-S3, C3 or C6 that uses the chip’s built-in USB, the “serial port” is provided by the chip itself. If the running firmware crashes in a tight loop, puts the chip into deep sleep, or reconfigures the USB pins, the port can disappear or stop responding.

Hold BOOT while plugging the board in, or while pressing reset. The chip then stays in the ROM, which always provides a working USB port. Expect the port to change name when you do this, because the board re-enumerates.

6. Power and baud rate

If the connection gets partway and then fails with timeouts, the board may be browning out when its radio or flash draws current. Unpowered USB hubs and long thin cables are the usual cause, so plug straight into the computer. Lowering the upload speed to 115200 also helps with marginal cables, at the cost of a slower flash.

A faster loop for debugging this

When I’m fighting a board, I switch to the browser-based ESP32 flasher. The steps are identical, but I don’t have to close the IDE’s monitor every time, and if the port is held by another program it says so instead of failing with a generic timeout. Once the board does connect, it shows the chip, MAC address and flash size, so I can confirm I’m talking to the board I think I am.

After a successful flash it resets the board and streams the boot log, which is where the next class of problems shows up: invalid headers, brownouts and boot loops. If you only want the output, the serial monitor does that on its own, and it reconnects automatically when a native-USB board drops off the bus during reset.