Troubleshooting

Common Issues

Here are some of the most common issues around the ESP32 development using Arduino.

Note

Please consider contributing if you have found any issues with the solution here.

Installing

Here are the common issues during the installation.

Slow or unstable downloads

Users in China might have troubles with connection and download speeds using GitHub. Please use our Jihulab mirror as the repository source:

https://jihulab.com/esp-mirror/espressif/arduino-esp32.git

JSON files for the boards manager are available here:

  • Stable release:

    https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_index_cn.json
    
  • Development release:

    https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_dev_index_cn.json
    

Building

Missing Python: “python”: executable file not found in $PATH

You are trying to build your sketch using Ubuntu and this message appears:

"exec: "python": executable file not found in $PATH
Error compiling for board ESP32 Dev Module"
Solution

To avoid this error, you can install the python-is-python3 package to create the symbolic links.

sudo apt install python-is-python3

If you are not using Ubuntu, you can check if you have the Python correctly installed or the presence of the symbolic links/environment variables.

Flashing

Why is my board not flashing/uploading when I try to upload my sketch?

To be able to upload the sketch via the serial interface, the ESP32 must be in the download mode. The download mode allows you to upload the sketch over the serial port, and to get into it, you need to keep the GPIO0 in LOW while resetting (EN pin) the cycle. If you are trying to upload a new sketch and your board is not responding, there are some possible reasons.

Possible fatal error message from the Arduino IDE:

A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header

Solution

Here are some steps that you can try:

  • Check your USB cable and try a new one (some cables are only for charging and there is no data connection).

  • Change the USB port - prefer direct connection to the computer and avoid USB hubs. Some USB ports may share the power source with other ports used, for example, for charging a phone.

  • Check your power supply.

  • Make sure that nothing is connected to pins labeled TX and RX. Please refer to the pin layout table - some TX and RX pins may not be labeled on the dev board.

  • In some instances, you must keep GPIO0 LOW during the uploading process via the serial interface.

  • Hold down the “BOOT” button on your ESP32 board while uploading/flashing.

  • Solder a 10 uF capacitor in parallel with RST and GND.

  • If you are using external power connected to pins, it is easy to confuse pins CMD (which is usually next to the 5V pin) and GND.

In some development boards, you can try adding the reset delay circuit, as described in the Power-on Sequence section on the ESP32 Hardware Design Guidelines to get into the download mode automatically.

Arduino IDE 1.x: {placeholder} expansion in upload recipes

This section documents how Arduino IDE 1.8.x (Java IDE) expands properties in platform.txt when you Upload a sketch. It explains why some patterns that work with arduino-cli or Arduino IDE 2.x fail on IDE 1.x, and records approaches we tried while wiring the flasher.py upload wrapper.

This does not apply to the compile step: builds use arduino-builder, which merges platform.txt and boards.txt much like arduino-cli. Only the upload step uses the Java path described below.

How IDE 1.8.x builds the upload command

On upload, SerialUploader (Arduino IDE 1.8.19) assembles a property map and runs StringReplacer on tools.<upload.tool>.upload.pattern:

  1. Start from PreferencesData.getMap() (IDE settings and a few runtime.* keys, such as runtime.platform.path).

  2. Merge board preferences from boards.txt (including menu selections), e.g. upload.speed, build.mcu.

  3. Merge only the active upload tool subtree: targetPlatform.getTool(upload.tool), which strips the tools.<tool>. prefix.

    Example: tools.esptool_py.cmd becomes the short key {cmd} inside the pattern.

  4. Replace {name} tokens in the pattern for up to 10 passes (StringReplacer.formatAndSplit).

So at upload time the recipe sees board prefs + one tool’s shorthands + runtime keys, not the full platform.txt file.

What works on IDE 1.x (reference patterns)

These patterns are known to expand correctly:

  • Board-scoped names from boards.txt: {upload.speed}, {upload.flags}, {build.mcu}, {upload.erase_cmd}, etc.

  • Runtime keys set when you select the board/port: {runtime.platform.path}, {serial.port}, {build.path} (set on upload).

  • Tool shorthands for the selected upload.tool only, e.g. {path}, {cmd}, {upload.pattern_args}, {network_cmd} (from tools.esptool_py.network_cmd).

The core uses upload.flash_prefix the same way as network_cmd: define it under each upload tool that needs the flasher wrapper, then reference {upload.flash_prefix} in upload.pattern / program.pattern:

tools.esptool_py.upload.flash_prefix=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.flash_prefix.windows="{runtime.platform.path}\tools\flasher.exe"
tools.esptool_py.upload.pattern={upload.flash_prefix} --esptool "{path}/{cmd}" --build-dir "{build.path}" {upload.pattern_args}
tools.esptool_py.program.pattern={upload.flash_prefix} --esptool "{path}/{cmd}" --build-dir "{build.path}" {program.pattern_args}

A second upload tool (esptool_py_app_only, used e.g. by the kode dot board) needs its own upload.flash_prefix because IDE 1.x never loads another tool’s keys during upload.

Approaches that do not work on IDE 1.x (and why)

The following are real configurations we tried for a single shared flasher command. They may work with arduino-cli or IDE 2.x but fail on IDE 1.8.x upload for the reasons given.

{tools.flasher.cmd} or a global tools.flasher.cmd property
tools.flasher.cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.pattern={tools.flasher.cmd} --esptool "{path}/{cmd}" ...

Result: Upload runs a program literally named {tools.flasher.cmd} (file not found).

Why: Only keys from the active tool subtree are merged as short names. Top-level tools.flasher.* is not part of getTool("esptool_py"). The full name tools.flasher.cmd is not in the upload property map at all.

{flasher.cmd} with tools.esptool_py.flasher.cmd duplicated on every upload tool
tools.esptool_py.flasher.cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py_app_only.flasher.cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.pattern={flasher.cmd} --esptool "{path}/{cmd}" ...

Result: Works on IDE 1.x, but the flasher command must be copied once per upload tool (and per OS variant).

Why: Same as above: each upload.tool value only exposes its tools.<that_tool>.* keys. There is no cross-tool alias.

{build.flasher.cmd} defined once in platform.txt
build.flasher.cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.pattern={build.flasher.cmd} --esptool "{path}/{cmd}" ...

Result: Unexpanded {build.flasher.cmd} in the executed command (or upload failure).

Why: getBoardPreferences() only contains build.* entries from boards.txt (and menus), not arbitrary build.* defaults from platform.txt. Unless every board defines build.flasher.cmd, the key is missing at upload time.

{upload.flasher_cmd} defined once at the top of platform.txt
upload.flasher_cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.pattern={upload.flasher_cmd} --esptool "{path}/{cmd}" ...

Result: Same as build.flasher.cmd: placeholder not expanded.

Why: Platform-level upload.* keys are not merged into board preferences. Only upload.* from boards.txt (e.g. esp32.upload.speed) appear in the upload map—compare with {upload.speed}, which works because boards define it.

Tool alias to a platform property: tools.esptool_py.upload.flasher_cmd={upload.flasher_cmd}
upload.flasher_cmd=python3 "{runtime.platform.path}/tools/flasher.py"
tools.esptool_py.upload.flasher_cmd={upload.flasher_cmd}
tools.esptool_py.upload.pattern={upload.flasher_cmd} --esptool "{path}/{cmd}" ...

Result: {upload.flasher_cmd} stays literal in the final command.

Why: After step 3 above, the tool map contains upload.flasher_cmd={upload.flasher_cmd} (a self-reference). The upload map still does not contain the resolved upload.flasher_cmd from the top of platform.txt, so StringReplacer never substitutes the Python path. arduino-cli avoids this by expanding the whole platform when loading properties; IDE 1.x upload does not.

kodedot.upload.flasher_cmd in boards.txt (board-only definition)
kodedot.upload.flasher_cmd=python3 "{runtime.platform.path}/tools/flasher.py"
kodedot.upload.tool=esptool_py_app_only
tools.esptool_py_app_only.upload.pattern={upload.flasher_cmd} --esptool "{path}/{cmd}" ...

Result: Can work for that one board.

Why: Board keys are merged in step 2. This only fixes boards that define the property; it duplicates the flasher path in boards.txt instead of platform.txt and does not help other boards or tools.

Inlining {runtime.platform.path}/tools/flasher.py in every upload.pattern
tools.esptool_py.upload.pattern=python3 "{runtime.platform.path}/tools/flasher.py" --esptool "{path}/{cmd}" ...
tools.esptool_py.program.pattern=python3 "{runtime.platform.path}/tools/flasher.py" --esptool "{path}/{cmd}" ...
tools.esptool_py_app_only.upload.pattern=python3 "{runtime.platform.path}/tools/flasher.py" --esptool "{path}/{cmd}" ...

Result: Works on IDE 1.x.

Why: {runtime.platform.path} is in the upload map. The downside is repeating the same flasher invocation in every pattern (and .windows variants), which is why upload.flash_prefix is preferred within each tool.

Summary

Approach

IDE 1.8.x upload

Note

{tools.*} global tool

No

Not in active tool map

{build.*} in platform only

No

Board map has no platform build.* defaults

{upload.*} in platform only

No

Board map has no platform upload.* defaults

Tool alias to platform key

No

Alias value never resolved

tools.<tool>.upload.* shorthand

Yes

One definition per upload tool

{runtime.platform.path} inline

Yes

Inline in pattern

Board upload.* in boards.txt

Yes

Per-board duplication

To have one flasher definition for the whole core you would need either a single upload tool for all boards or a change in IDE 1.x (not something this core can assume). The maintained approach is upload.flash_prefix per upload tool, mirroring network_cmd.

A small script in the core tree simulates IDE 1.8.19 upload expansion for regression checks (run from the esp32 hardware folder):

python3 .github/scripts/ci_testing/test_ide1_upload_pattern.py

Hardware

Why is my computer not detecting my board?

If your board is not being detected after connecting to the USB, you can try the following:

Solution
  • Check if the USB driver is missing. - USB Driver Download Link

  • Check your USB cable and try a new one.

  • Change the USB port.

  • Check your power supply.

  • Check if the board is damaged or defective.

Wi-Fi

Why does the board not connect to WEP/WPA-“encrypted” Wi-Fi?

Please note that WEP/WPA has significant security vulnerabilities, and its use is strongly discouraged. The support may, therefore, be removed in the future. Please migrate to WPA2 or newer.

Solution

Nevertheless, it may be necessary to connect to insecure networks. To do this, the security requirement of the ESP32 must be lowered to an insecure level by using:

WiFi.setMinSecurity(WIFI_AUTH_WEP); // Lower min security to WEP.
// or
WiFi.setMinSecurity(WIFI_AUTH_WPA_PSK); // Lower min security to WPA.

Why does the board not connect to WPA3-encrypted Wi-Fi?

WPA3 support is resource-intensive and may not be compiled into the used SDK.

Solution
  • Check WPA3 support by your SDK.

  • Compile your custom SDK with WPA3 support.

Sample code to check SDK WPA3 support at compile time:

#ifndef CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE
#warning "No WPA3 support."
#endif

Serial not printing

I have uploaded firmware to the ESP32 device, but I don’t see any response from a Serial.print (HardwareSerial).

Solution

Newer ESP32 variants have two possible USB connectors - USB and UART. The UART connector will go through a USB->UART adapter, and will typically present itself with the name of that mfr (eg, Silicon Labs CP210x UART Bridge). The USB connector can be used as a USB-CDC bridge and will appear as an Espressif device (Espressif USB JTAG/serial debug unit). On Espressif devkits, both connections are available, and will be labeled. ESP32 can only use UART, so will only have one connector. Other variants with one connector will typically be using USB. Please check in the product [datasheet](https://products.espressif.com) or [hardware guide](https://www.espressif.com/en/products/devkits) to find Espressif products with the appropriate USB connections for your needs. If you use the UART connector, you should disable USB-CDC on boot under the Tools menu (-D ARDUINO_USB_CDC_ON_BOOT=0). If you use the USB connector, you should have that enabled (-D ARDUINO_USB_CDC_ON_BOOT=1) and set USB Mode to “Hardware CDC and JTAG” (-D ARDUINO_USB_MODE=1). USB-CDC may not be able to initialize in time to catch all the data if your device is in a tight reboot loop. This can make it difficult to troubleshoot initialization issues.

SPIFFS mount failed

When you come across an error like this:

E (588) SPIFFS: mount failed, -10025
[E][SPIFFS.cpp:47] begin(): Mounting SPIFFS failed! Error: -1

Try enforcing format on fail in your code by adding true in the begin method such as this:

SPIFFS.begin(true);

See the method prototype for reference: bool begin(bool formatOnFail=false, const char * basePath="/spiffs", uint8_t maxOpenFiles=10, const char * partitionLabel=NULL);

SD card mount fail

Even though you made sure that the pins are correctly connected, and not using restricted pins, you may still get an error such as this:

[ 1065][E][sd_diskio.cpp:807] sdcard_mount(): f_mount failed: (3) The physical drive cannot work

Most of the problems originate from a poor connection caused by prototyping cables/wires, and one of the best solutions is to solder all the connections or use good quality connectors.

Note that with SD_MMC lib all the data pins need to be pulled up with an external 10k to 3.3V. This applies especially to card’s D3 which needs to be pulled up even when using 1-bit line connection and the D3 is not used.

If you want to try the software approach before soldering, try manually specifying SPI pins, like this:

int SD_CS_PIN = 19;
SPI.begin(18, 36, 26, SD_CS_PIN);
SPI.setDataMode(SPI_MODE0);
SD.begin(SD_CS_PIN);

ESP32-S3 is rebooting even with a bare minimum sketch

Some ESP32-S3 boards are equipped with Quad SPI (QSPI) or Octal SPI (OPI) PSRAM. If you upload such a board with default settings for ESP32-S3, it will result in rebooting with a message similar to this:

https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/flash_psram_config.html

E (124) esp_core_dump_flash: Core dump flash config is corrupted! CRC=0x7bd5c66f instead of 0x0
Rebooting...
⸮⸮⸮ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x18 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40376af0
SPIWP:0xee
Octal Flash Mode Enabled
For OPI Flash, Use Default Flash Boot Mode
mode:SLOW_RD, clock div:1
load:0x3fce3808,len:0x44c
load:0x403c9700,len:0xbec
load:0x403cc700,len:0x2920
entry 0x403c98d8

assert failed: do_core_init startup.c:326 (flash_ret == ESP_OK)

To fix the issue, you will need to find out the precise module you are using and set PSRAM in the Arduino IDE Tools according to the following table.

How to determine the module version:

  • First determine if you have a WROOM-1 or WROOM-2 module - this is written on the module shielding almost at the top, right under the ESP logo and company name (Espresif) right after the ESP32-S3 - for example ESP32-S3-WROOM-2.

  • Then locate the version code on left bottom corner on the module shielding. The markings are very small and it might be really difficult to read with naked eyes - try using a camera with careful lighting.

With this knowledge find your module in the table and note what is written in the PSRAM column.

  • If the results is empty (-) you don’t need to change anything

  • For QSPI go to Tools > PSRAM > QSPI PSRAM

  • For OPI go to Tools > PSRAM > OPI PSRAM

Note that WROOM-2 has always OPI.

Module

Code

Flash Mode

PSRAM

WROOM-1

N4

QSPI

WROOM-1

N8

QSPI

WROOM-1

N16

QSPI

WROOM-1

H4

QSPI

WROOM-1

N4R2

QSPI

QSPI

WROOM-1

N8R2

QSPI

QSPI

WROOM-1

N16R2

QSPI

QSPI

WROOM-1

N4R8

QSPI

OPI

WROOM-1

N8R8

QSPI

OPI

WROOM-1

N16R8

QSPI

OPI

WROOM-2

N16R8V

OPI

OPI

WROOM-2

N16R8V

OPI

OPI

WROOM-2

N32R8V

OPI

OPI

Further Help

If you encounter any other issues or need further assistance, please consult the ESP32 Arduino Core documentation or seek help from the ESP32 community forums.