Phase 1: Beginner 13 min read read

ESP32 Boot Strapping Pins Explained: GPIO 0, 2, 5, 12, and 15

Learn how ESP32 boot strapping pins work, what each pin controls at power-on, and how to avoid boot loops caused by incorrect GPIO voltage levels during startup.

Updated June 18, 2026

What Happens in the First Milliseconds After Power-On

When you apply power or press the reset button on an ESP32, a small piece of code permanently burned into the chip’s ROM (read-only memory) runs before any of your Arduino sketch executes. This ROM bootloader performs a sequence of checks: it tests the integrity of the chip’s clock, samples a handful of GPIO pins to determine the boot configuration, and then decides whether to load firmware from flash or wait for new firmware over UART. The GPIO pins it samples during this window are the boot strapping pins.

The sampling happens at the rising edge of the reset signal — the moment EN/RST goes from low to high. At that precise instant the ROM reads the logic level on GPIO 0, GPIO 2, GPIO 5, GPIO 12, and GPIO 15. These levels are set by internal pull resistors inside the chip, combined with any external wiring or devices you have connected. If an external component holds one of these pins at the wrong voltage, the ROM may select an unintended boot mode, select the wrong flash voltage, or behave unpredictably.

The Five Strapping Pins and Their Roles

GPIO 0 — Boot Mode (SPI Boot vs UART Download)

GPIO 0 is the primary boot mode selector. When GPIO 0 is high at reset, the ESP32 enters SPI Boot mode and loads your firmware from the SPI flash chip. When GPIO 0 is low at reset, the ESP32 enters UART Download mode and runs a serial bootloader that waits for new firmware from a host computer using the esptool.py protocol.

The internal state of GPIO 0 is a weak pull-up. The DevKitC adds a 10 kΩ external pull-up to 3.3 V to reinforce this default. The BOOT button on the DevKit shorts GPIO 0 to GND — you hold BOOT, press RST, release RST, then release BOOT to enter download mode manually. When using the Arduino IDE with a DevKitC, the USB-to-serial chip (CP2102 or CH340) automates this sequence using the DTR and RTS handshake lines, which is why pressing Upload works without manual button pressing on quality boards.

Problems caused by GPIO 0: Any external component that holds GPIO 0 low at power-on will trap the ESP32 in download mode. The most common culprits are I²C or SPI sensors connected with pull-down resistors, RC timing circuits, or MOSFET gate drivers whose output is low until the control signal rises. If your ESP32 appears to not run any sketch but accepts programming normally, check what is connected to GPIO 0.

GPIO 2 — Boot Validation (Secondary UART Mode)

GPIO 2 participates in boot mode configuration alongside GPIO 0. Specifically, when GPIO 0 is low (download mode), GPIO 2 must also be low for the download to work correctly on some chip revisions. When GPIO 0 is high (normal boot), GPIO 2 must not be pulled high externally on module configurations that include a pull-down in the boot sequence validation.

On the DevKitC, GPIO 2 connects to the blue LED through a resistor. The LED provides a small but non-zero load that typically holds the pin low enough during boot (the LED’s forward voltage drop keeps the pin below the logic-high threshold when not driven). On custom boards, a pull-up to 3.3 V on GPIO 2 can prevent normal booting. The safest approach: leave GPIO 2 floating or with a weak pull-down, and drive it only from your sketch after boot confirms via LED blink or Serial.print.

GPIO 5 — SDIO Slave Timing

GPIO 5 controls the SDIO slave timing configuration at boot. When GPIO 5 is high, the SDIO slave interface uses the falling edge for data capture; when low, it uses the rising edge. For most projects that do not use the SDIO interface, this distinction is irrelevant — but the pin still needs to be high during boot to match the module defaults, otherwise the chip may initialise with an incorrect internal peripheral configuration.

The internal state is a pull-up, and the DevKitC reinforces this with a 10 kΩ external pull-up. GPIO 5 is also the default VSPI CS0 (chip-select) line, which happens to default-high — a convenient coincidence for SPI designs. After boot, drive GPIO 5 freely. Avoid connecting it to any device that pulls it low before the ESP32 powers on.

GPIO 12 — Flash Voltage Selection (Most Dangerous)

GPIO 12 is the most consequential strapping pin for hardware developers. It controls the voltage supplied to the SPI flash chip that holds your firmware. The internal state is a pull-down, selecting 3.3 V for the flash supply — correct for the GD25Q32, W25Q32, and similar flash chips used in WROOM-32, WROOM-32D, and WROVER modules. When GPIO 12 reads high at boot, the chip reconfigures the internal LDO to supply 1.8 V to the flash instead.

If your module uses 3.3 V flash (virtually all standard WROOM-32 modules do) and GPIO 12 is pulled high at boot, the flash operates at 1.8 V — below its rated minimum. The chip may read corrupted firmware, fail to boot, or enter a reset loop. This condition is silent and extremely confusing if you do not know the root cause.

The practical rules for GPIO 12: never connect a pull-up resistor to GPIO 12; use it only as a floating input or driven output; add a comment in your schematic noting the boot constraint. If you are using a module that genuinely uses 1.8 V flash (such as certain ESP32-D0WD-V3 based modules intended for low-voltage designs), you would intentionally pull GPIO 12 high — but verify this against your specific module’s datasheet before doing so.

GPIO 15 — Boot Log Suppression

GPIO 15 has the least dramatic effect of the five strapping pins. When high (default, internal pull-up), the ROM bootloader prints a log of its startup sequence to UART0 at 115200 baud. This log includes the firmware load address, chip revision, flash size, and other diagnostic information. When GPIO 15 is low at boot, this log is suppressed.

Log suppression is useful in production devices where UART0 is exposed through an external connector — you do not want the boot log confusing a host device that expects application-level serial protocol from the start. During development, GPIO 15 high (or floating) is almost always the right choice because the boot log helps diagnose flash errors and brownout resets. After boot, GPIO 15 functions as a regular GPIO with ADC2 capability.

Default Strapping Summary

Pin Internal Default Low Selects High Selects
GPIO 0 Pull-up (High) UART Download Mode SPI Boot Mode ✓
GPIO 2 Pull-down (Low) Normal boot ✓ Avoid externally
GPIO 5 Pull-up (High) Avoid (SDIO timing) Normal boot ✓
GPIO 12 Pull-down (Low) 3.3 V flash ✓ 1.8 V flash (danger)
GPIO 15 Pull-up (High) Boot log off Boot log on ✓

How Development Boards Handle Strapping Automatically

The DevKitC and NodeMCU-32S boards incorporate resistors and capacitors that keep all five strapping pins at their correct default values during power-on without requiring any user action. The 10 kΩ pull-ups on GPIO 0 and GPIO 5, the onboard LED providing a soft load on GPIO 2, and the absence of external pull-ups on GPIO 12 all contribute to reliable booting out of the box.

Custom PCB developers must replicate these conditions. The minimum strapping network for a stable WROOM-32 design is: 10 kΩ pull-up on GPIO 0 to 3.3 V, 10 kΩ pull-up on GPIO 5 to 3.3 V, GPIO 12 left floating with no external resistor, GPIO 15 left floating (or connected to a 10 kΩ pull-up if you want boot log). GPIO 2 should have a 10 kΩ pull-down or be left floating — never pull it up.

Production Firmware and eFuse Overrides

For devices deployed in the field, relying on external resistors to set strapping pin levels can be fragile. Espressif provides an eFuse mechanism to permanently override some boot defaults inside the chip itself. The JTAG_SEL_ENABLE and DL_ENABLE eFuses can disable download mode (preventing firmware extraction from stolen devices) and lock the flash voltage selection (removing the GPIO 12 dependency entirely). Burning eFuses is irreversible — once written they cannot be reset — so this is a step for final production firmware, not development.

Diagnosing Boot Strapping Problems

If your ESP32 is stuck in a boot loop or download mode, follow this checklist:

  1. Disconnect all GPIO wiring except power and USB. Press RST. Does it boot? If yes, reconnect devices one by one to identify which pin causes the problem.
  2. Connect a USB serial adapter and open a terminal at 115200 baud. A boot loop typically prints a repeating “rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)” sequence. A flash voltage mismatch may print no output at all or just garbage.
  3. Measure GPIO 0 voltage at the moment you apply power (not after boot). It should be above 2.0 V for SPI Boot mode. If it reads below 0.8 V, something is holding it low.
  4. Measure GPIO 12 at power-on. It should be below 0.5 V on a WROOM-32. Any voltage above 1.0 V is suspicious.
  5. Try isolating the ESP32 on a breadboard with only power and USB. If it boots cleanly, the problem is in your connected circuit rather than the chip itself.

Frequently Asked Questions

Projects to Build

Put this knowledge to work — try one of these hands-on projects.