Why GPIO Safety Matters Before You Wire Anything
One of the most common frustrations for ESP32 beginners is a board that boots into an endless reset loop or refuses to accept a firmware upload — and the cause is almost always a GPIO misused as an output, a pull-up on the wrong pin, or a wire connected to a flash SPI pin. Unlike AVR-based Arduinos where you can safely use most digital pins interchangeably, the ESP32 has pins that influence boot mode, pins that are hardwired to internal peripherals, and pins that are physically incapable of driving output. Knowing these before you place your first wire saves hours of debugging.
This guide gives you a clear mental model for safe pin selection organised into four categories: pins that are always safe to use, pins with boot-time constraints, input-only pins, and pins that should never be touched.
Category 1: Always-Safe Output and Input Pins
These GPIO pins can be freely used as digital input, digital output, PWM, UART, SPI, or I²C without any boot constraints or hidden conflicts on a standard 38-pin DevKitC running WROOM-32:
- GPIO 16, 17 — UART2 (RX, TX). Safe for any use; excellent for connecting GPS, GSM, or other serial devices without interfering with the USB programming port.
- GPIO 18, 19, 23 — Default VSPI clock, MISO, MOSI. Ideal for SPI devices (SD cards, displays, radio modules). Safe as general GPIO when not used for SPI.
- GPIO 21, 22 — Default I²C SDA and SCL. Safe for any use. Nearly every I²C sensor library defaults to these pins.
- GPIO 25, 26 — DAC1 and DAC2. The only GPIO with true analog output capability. Also ADC2 channels (avoid analogRead on these with Wi-Fi active).
- GPIO 27 — No conflicts, no boot constraints. ADC2 channel and touch pin T7. Reliable for any general digital use.
- GPIO 32, 33 — ADC1 channels CH4 and CH5. Touch pins T9 and T8. Can connect to a 32.768 kHz crystal for RTC. These are the best pins for analog reads in Wi-Fi projects because ADC1 does not conflict with the Wi-Fi radio.
If you are building a new project and unsure which pin to use, start from this list and work outward only if you run out of pins.
Category 2: Usable After Boot — Handle Boot-Time Carefully
These pins are perfectly functional in running firmware but must not be externally driven high or low during the power-on boot sequence, because they influence the ESP32’s startup configuration.
GPIO 0 — Boot Mode Selector
GPIO 0 is held high by an internal pull-up (and an external 10 kΩ on the DevKitC). Grounding it at power-up puts the chip into download (programming) mode. In running firmware you can use it freely — the onboard BOOT button is wired to it for exactly this reason. Avoid connecting external circuits that might pull GPIO 0 low at power-up. A momentary switch that grounds it is safe as long as you are not pressing it when power is applied.
GPIO 2 — Blue LED, Secondary Boot Strapping
GPIO 2 participates in the boot sequence together with GPIO 0. On some module variants it must be low (or floating) during boot to initiate normal operation. The DevKitC handles this automatically, but if you have a pull-up resistor wired to GPIO 2 externally and the chip was not booting before — this pin is your first suspect. In running code it works perfectly; toggling it blinks the blue onboard LED.
GPIO 5 — SDIO Slave Timing
GPIO 5 must be high during boot (controls SDIO slave timing). The DevKitC has a 10 kΩ pull-up. Do not connect GPIO 5 to a device that holds it low before power-on. After boot, use it freely — it is a common choice for SPI chip-select because it defaults high (deselecting the SPI device until you actively pull it low).
GPIO 12 — Flash Voltage Strapping
This is the most dangerous safe pin. GPIO 12 sets the internal flash operating voltage at boot: floating or low selects 3.3 V flash (correct for WROOM-32 modules), high selects 1.8 V flash (used only in certain embedded module variants). Never connect a pull-up resistor to GPIO 12 externally on a standard WROOM-32 or WROVER module. Use it as input or output only, with no pull-up. Read the warning in your project documentation before adding anything to this pin.
GPIO 15 — Boot Log Enable
GPIO 15 controls whether the ROM bootloader prints startup messages to UART0. When high (default with internal pull-up), the boot log is printed. When low, it is suppressed — sometimes used in production firmware to hide device information. Use GPIO 15 freely in running code, but be aware that pulling it low may suppress boot messages that help diagnose startup failures.
Category 3: Input-Only Pins
GPIO 34, 35, 36 (VP), and 39 (VN) have no output driver circuit. pinMode(34, OUTPUT) compiles without error but does nothing — the pin cannot drive a signal. They also have no internal pull-up or pull-down resistors. Any external switch or sensor connected to these pins needs its own pull resistor.
Despite these limitations, the input-only pins are excellent for their intended purpose: reading analog voltages via ADC1. GPIO 36 (VP, ADC1_CH0) and GPIO 39 (VN, ADC1_CH3) are particularly valuable because ADC1 works reliably while Wi-Fi is active, making them the default pins for battery voltage monitoring dividers, light sensor readings, and pot inputs in Wi-Fi-connected projects.
Category 4: Pins You Must Never Use
GPIO 6, 7, 8, 9, 10, and 11 are internally wired to the quad-SPI flash interface. On the 38-pin DevKitC these six pins are physically missing from the pin headers — Espressif intentionally omitted them to prevent the very mistake this section warns about. On bare ESP32 chips wired into custom PCBs, these pads exist and can be mistakenly connected to external components. If you ever encounter an ESP32 that enters a boot loop regardless of sketch state, check whether any wire is touching these physical pads or any net connected to them on your PCB.
GPIO 1 and GPIO 3: Serial Console Overlap
GPIO 1 (TX0) and GPIO 3 (RX0) are not in the “never use” category, but they are frequently overloaded by the Arduino Serial library. As long as your sketch calls Serial.begin(), these pins are owned by UART0 and will output garbage or miss data if you try to use them for other purposes simultaneously. If you need all 28 GPIO on the DevKitC, you can disable Serial and reclaim GPIO 1 and 3 — but you lose the Serial Monitor for debugging, which makes development significantly harder. The practical advice: avoid GPIO 1 and 3 during development; reassign them only after the project is stable and tested.
Safe Pin Selection Cheatsheet
| Category | Pins | Notes |
|---|---|---|
| Always safe | 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33 | No restrictions in normal operation |
| Safe after boot | 0, 2, 4, 5, 12, 13, 14, 15 | Mind external circuits at power-on |
| Input only | 34, 35, 36, 39 | No output, no internal pull resistors |
| Serial conflict | 1, 3 | Shared with USB programming UART |
| Never use | 6, 7, 8, 9, 10, 11 | Internal flash SPI — will crash ESP32 |
Practical Rules for New Projects
Follow these rules and you will avoid the most common GPIO-related mistakes:
- Start your pin list from the “always safe” category and work outward only if you need more pins.
- Never wire pull-up resistors to GPIO 0, 2, 5, or 12 before understanding whether they are boot-strapping pins for your specific module variant.
- Use GPIO 32 or 33 for your first ADC sensor — they are in ADC1 and produce reliable readings alongside Wi-Fi.
- Assign UART2 (GPIO 16 RX, 17 TX) for any serial sensor like GPS or GSM, keeping UART0 (GPIO 1/3) free for the Serial Monitor during development.
- If a circuit requires GPIO 12, add a note to your project documentation — every future maintainer needs to know it is a boot-sensitive pin.
- For input-only pins (34, 35, 36, 39), always add an external pull resistor if connecting a switch or button; these pins float freely without one.