The ESP32 Strapping Pins List: GPIO0, GPIO2, GPIO4, GPIO5, GPIO12, GPIO15 Explained

When you wire a new sensor or relay to an ESP32 and the board suddenly refuses to boot, you have almost certainly crossed paths with a strapping pin. During power-on reset, the ESP32 samples six specific GPIO pins to determine its boot mode, SPI flash voltage, and serial log output. If your external circuit pulls any of these pins to the wrong logic level during the first few milliseconds of boot, the chip will halt, enter download mode, or misconfigure its internal voltage regulators.

Here is the definitive esp32 strapping pins list gpio0 gpio2 gpio4 gpio5 gpio12 gpio15 and exactly what they control, based on the official Espressif ESP32 Datasheet.

ESP32 Strapping Pins Specification Sheet
GPIO Pin Internal State at Boot Function When LOW Function When HIGH
GPIO0 Pull-up Enter UART Download Mode Boot from SPI Flash (Normal)
GPIO2 Pull-down Boot from SPI Flash (Normal) Enter SDIO Boot Mode
GPIO4 Pull-down SDIO Data Line (Normal) SDIO Power-up Sequence
GPIO5 Pull-up SDIO Timing / Silent Boot Log Normal Boot Log Output
GPIO12 (MTDI) Pull-down Flash VDD_SDIO = 3.3V (Normal) Flash VDD_SDIO = 1.8V (Danger)
GPIO15 (MTDO) Pull-up Silent Boot Log (No Serial) Normal Boot Log Output
⚠️ Bench Warning: The GPIO12 Trap
GPIO12 is the most common silent killer of ESP32 projects. If you wire a sensor with a pull-up resistor to GPIO12, the chip reads it HIGH at boot and configures its internal LDO to supply 1.8V to the external SPI flash. Because the WROOM-32 module uses a 3.3V flash chip, the flash starves for voltage, and the boot process crashes. Never use GPIO12 for external inputs unless you are absolutely certain of its logic state at power-on.

Why Your ESP32 Fails to Boot: Exact Error Strings and Ranked Causes

When a strapping pin conflict occurs, the ESP32 serial monitor (at 115200 baud) will throw specific error strings. Before you rewrite your code, check the hardware. Here are the first three things to check when your board fails to boot:

  1. Disconnect all peripherals wired to GPIO 0, 2, 4, 5, 12, and 15. Power cycle the board. If it boots normally, your external circuit is dragging a strapping pin to the wrong state.
  2. Swap the USB cable and power source. Plug directly into a motherboard USB port, not an unpowered hub. Voltage sag during the initial WiFi radio calibration triggers false brownouts.
  3. Force Download Mode. If the flash is corrupted or stuck, press and hold the BOOT button (hardwired to GPIO0), tap the EN button, then release BOOT to force the chip into UART upload mode.

Exact Error Strings and Ranked Causes

According to the Espressif Bootloader Documentation, these are the exact strings you will see and how to fix them.

Error String: Brownout detector was triggered

  • Cause 1 (Most Likely): USB cable has high resistance, causing the 3.3V rail to drop below 2.4V when the WiFi radio powers on.
  • Cause 2: A 5V relay module wired to the ESP32's 5V/VIN pin is kicking in simultaneously with boot, collapsing the shared power rail.
  • Fix: Use a dedicated 5V 2A power supply for relays, sharing only the GND with the ESP32. Use a high-quality data+power USB cable.

Error String: flash read err, 1000

  • Cause 1: GPIO12 is being pulled HIGH at boot, switching the flash voltage to 1.8V.
  • Cause 2: GPIO0 and GPIO2 are both in an invalid combination for boot modes.
  • Fix: Remove any pull-up resistors or sensors connected to GPIO12. Ensure GPIO2 is not pulled HIGH (it has an internal pull-down, so external pull-ups will break normal SPI boot).

Error String: waiting for download

  • Cause 1: GPIO0 is being held LOW at power-on. This is the hardware UART download trigger.
  • Fix: If you have a button on GPIO0, ensure it uses a pull-up resistor and is not pressed during power-up. If a sensor is on GPIO0, add a 10kΩ pull-up resistor to 3.3V to guarantee a HIGH state during the boot window.

Decision Tree: Which GPIO Should You Actually Use?

To avoid strapping pin issues entirely, use this decision table to select safe GPIOs for your peripherals. This path terminates in concrete, safe pin selections for the standard ESP32-WROOM-32.

GPIO Selection Decision Matrix
Peripheral Need Constraint / Requirement Concrete GPIO Pick (Safe)
Tactile Button / Switch Needs internal pull-up, no boot conflicts GPIO 32, 33, 34, 35, 36, or 39
Relay Module / LED Output only, must not glitch at boot GPIO 16, 17, 18, 19, 21, 22, 23, 25, 26, or 27
I2C Sensor (BME280, OLED) Needs hardware I2C default mapping GPIO 21 (SDA) and GPIO 22 (SCL)
Analog Sensor (ADC) Must work concurrently with WiFi GPIO 32, 33, 34, 35, 36, 39 (ADC1 only)
Capacitive Touch Needs internal touch sensor hardware GPIO 4, 12, 13, 14, 15, 27, 32, 33

Default Recommendation: If you are prototyping and just need a generic output pin that is guaranteed to be safe from boot glitches, default to GPIO 25, 26, or 27. They have no internal boot dependencies and drive outputs cleanly.

Hardware Build: Safe Relay & Button Wiring on the ESP32-WROOM-32

Let us build a practical circuit that completely avoids the strapping pins. We will wire a debounced button to trigger a 5V relay module.

Parts List

  • Microcontroller: ESP32-WROOM-32 DevKit v1 (30-pin variant)
  • Relay: Songle SRD-05VDC-SL-C 5V Relay Module (Opto-isolated, active LOW)
  • Input: 12x12mm Tactile Switch
  • Resistor: 10kΩ (for external pull-up on the button, optional but recommended for noise immunity)
  • Power: 5V 2A USB Power Supply

Pin Mapping Table

Component Component Pin ESP32 GPIO Why this pin?
Tactile Switch Signal GPIO 33 Safe input pin, supports internal pull-up, no boot conflict.
Relay Module IN (Signal) GPIO 26 Safe output pin, no boot glitch, defaults to LOW safely.
Relay Module VCC VIN (5V) Relay needs 5V. Do not power from the 3.3V pin.
Relay Module GND GND Shared ground reference.

Complete Compilable Code: Debounced Button & Relay Control

This code targets the ESP32 Dev Module board variant in the Arduino IDE (using the official esp32 board package by Espressif, v2.0.x or v3.0.x). It includes explicit pin definitions, software debouncing to prevent relay chatter, and serial error handling to verify boot states.

/*
 * ESP32 Safe GPIO Relay Control
 * Target Board: ESP32 Dev Module (ESP32-WROOM-32)
 * Avoids all strapping pins (GPIO 0, 2, 4, 5, 12, 15)
 */

// --- PIN DEFINITIONS ---
// GPIO 33: Safe input pin (ADC1_CH5, Touch8, no boot conflict)
#define BUTTON_PIN 33
// GPIO 26: Safe output pin (DAC2, no boot conflict)
#define RELAY_PIN  26

// --- TIMING CONSTANTS ---
#define DEBOUNCE_DELAY_MS 50

// --- STATE VARIABLES ---
bool lastButtonState = HIGH;
bool currentButtonState = HIGH;
bool relayState = false;
unsigned long lastDebounceTime = 0;

void setup() {
  // Initialize Serial for boot diagnostics
  Serial.begin(115200);
  unsigned long bootTime = millis();
  
  // Configure pins explicitly
  // Using INPUT_PULLUP eliminates the need for an external 10k resistor
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  
  // Relay modules are typically Active LOW
  // Set HIGH first to ensure relay is OFF before setting as output
  digitalWrite(RELAY_PIN, HIGH);
  pinMode(RELAY_PIN, OUTPUT);

  // Boot verification
  Serial.println("-----------------------------------");
  Serial.println("ESP32 Boot Successful.");
  Serial.print("Free heap memory: ");
  Serial.println(ESP.getFreeHeap());
  Serial.println("Safe GPIOs in use: 33 (Input), 26 (Output)");
  Serial.println("Strapping pins avoided.");
  Serial.println("-----------------------------------");
}

void loop() {
  // Read the raw state of the button
  bool reading = digitalRead(BUTTON_PIN);

  // Debounce logic: check if the state changed and if enough time has passed
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY_MS) {
    // If the button state has actually changed
    if (reading != currentButtonState) {
      currentButtonState = reading;

      // Trigger relay only on the falling edge (button press, LOW)
      if (currentButtonState == LOW) {
        relayState = !relayState; // Toggle state
        
        // Active LOW relay logic
        if (relayState) {
          digitalWrite(RELAY_PIN, LOW);  // Turn ON
          Serial.println("[ACTION] Relay ENGAGED (GPIO 26 LOW)");
        } else {
          digitalWrite(RELAY_PIN, HIGH); // Turn OFF
          Serial.println("[ACTION] Relay DISENGAGED (GPIO 26 HIGH)");
        }
      }
    }
  }

  // Save the raw reading for the next loop iteration
  lastButtonState = reading;
}
💡 Pro Tip: Active LOW Relays
Most 5V relay modules sold for Arduino/ESP32 are "Active LOW". This means the opto-isolator LED turns on when the signal pin is pulled to GND. Notice in the setup() function above, we write the pin HIGH before setting it as an OUTPUT. This prevents the relay from briefly clicking on during the ESP32 boot sequence while the GPIO is floating.

Extending and Simplifying the Build

Once your base circuit is stable and booting reliably, you can scale the project up or down based on your deployment environment.

How to Extend: Adding I2C Sensors

If you need to add environmental monitoring (like a BME280 or an SSD1306 OLED display), use the default hardware I2C pins: GPIO 21 (SDA) and GPIO 22 (SCL). Neither of these are strapping pins, and the ESP32 Arduino core maps the Wire library to them by default. Do not attempt to bit-bang I2C on GPIO 4 or 5, as their boot-time state changes will disrupt the display or sensor initialization.

How to Simplify: Deep Sleep for Battery Power

If you are moving this build to a LiFePO4 or 18650 battery pack, you must minimize quiescent current. The WiFi radio draws ~240mA during transmission. Simplify the build by replacing the loop() with an interrupt-driven deep sleep cycle.

  • Wire the button to GPIO 33.
  • Use esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0) to wake the chip only when the button is pressed.
  • Execute the relay toggle, then immediately call esp_deep_sleep_start().
  • This drops the idle current from ~80mA (WiFi connected) to roughly 10µA, extending a 3000mAh 18650 cell from a few days to over a year.

By strictly adhering to the safe GPIO list and understanding the exact hardware mechanisms behind the esp32 strapping pins list gpio0 gpio2 gpio4 gpio5 gpio12 gpio15, you eliminate the most frustrating class of embedded debugging: hardware-software boot conflicts. Wire to GPIO 25, 26, 27, 32, or 33 by default, and your ESP32 will boot cleanly every single time.