Beginner7 min readESP32

ESP32 GPIO Pin Guide: Which Pins Are Safe to Use

Strapping pins, flash pins, input-only pins, ADC1 vs ADC2, touch pins and the default I2C and SPI pins on the original ESP32 (WROOM-32), with a table of the pins to use first.

An ESP32 dev board has around 30 header pins labelled with GPIO numbers, but many of them come with conditions. Some decide how the chip boots, some are already wired to the flash memory, and some can only read, never drive. Pick the wrong one and you get a board that won't boot, won't accept uploads, or crashes at random. This guide covers the original ESP32 (the ESP32-D0WD chip in WROOM-32 and WROVER modules), as found on the ESP32-DevKitC and "ESP32 DEVKIT V1" boards.

Watch out

ESP32-S2, S3, C3, C6 and other family members are different chips with different pin rules: other strapping pins, other flash pins, other ADC channel layouts and, on some, native USB pins. None of the pin numbers below carry over. Check the datasheet for your chip, and the datasheet for your module, which may use extra pins for flash and PSRAM.

GPIOVerdictNotes
4, 13, 27GoodGeneral purpose. ADC2 and touch capable (analog only without Wi-Fi).
14Good, not for relaysLike the row above, but it briefly outputs a signal during boot, which can click a relay or flash an LED at every reset.
16, 17GoodNot available on WROVER boards (used by PSRAM).
18, 19, 23GoodDefault SPI pins (SCK, MISO, MOSI). Free if you don't use SPI.
21, 22GoodDefault I2C pins (SDA, SCL).
25, 26GoodAlso the two DAC outputs. ADC2.
32, 33GoodADC1 and touch. The only ADC1 pins that can also be outputs.
34, 35, 36, 39Input onlyADC1. No output and no internal pull-up or pull-down.
5Usable with careStrapping pin, HIGH during boot. Default SPI chip select.
2Usable with careStrapping pin; must not be HIGH when flashing. LED on many boards.
15Usable with careStrapping pin; LOW at reset silences the boot log.
12Avoid if you canStrapping pin; HIGH at reset stops a WROOM-32 from booting.
0AvoidStrapping pin; LOW at reset enters download mode (the BOOT button).
1, 3AvoidUART0 TX and RX, connected to the USB-serial chip.
6–11NeverConnected to the SPI flash chip.

GPIO numbers and voltages

In Arduino code you use the GPIO number: digitalWrite(23, HIGH) drives GPIO23, whether the silkscreen says D23, IO23, G23 or 23. The chip exposes GPIO0–19, 21–23, 25–27 and 32–39. There is no GPIO20, 24 or 28–31 on a WROOM-32.

All ESP32 GPIOs are 3.3 V and not 5 V tolerant, so use a level shifter or a resistor divider for 5 V signals. They are also meant for signals, not loads: drive relays and motors through a transistor or driver module.

Strapping pins: GPIO0, 2, 5, 12 and 15

The chip reads these five pins at the moment it comes out of reset (power-on or the EN button) and uses their levels to configure itself. Each one has an internal pull resistor that sets the default. If your circuit overpowers that default during reset, the chip boots differently.

GPIODefault at resetWhat it selectsWhat goes wrong
0Pulled up (HIGH)LOW: serial download mode. HIGH: run the program in flash.Held LOW at reset, the board waits for an upload instead of running your sketch. The BOOT button does exactly this.
2Pulled down (LOW)Must be LOW or floating to enter download mode.Held HIGH by external circuitry, uploads fail.
5Pulled up (HIGH)SDIO slave timing (together with GPIO15).Rarely matters in Arduino projects, but the pin sits HIGH during boot.
12 (MTDI)Pulled down (LOW)Flash supply voltage. LOW: 3.3 V. HIGH: 1.8 V.Held HIGH at reset, the 3.3 V flash in a WROOM-32 gets 1.8 V and the module fails to boot.
15 (MTDO)Pulled up (HIGH)LOW silences the boot messages the ROM prints on UART0.Mostly harmless: you just lose the boot log.

What this means in practice:

  • Using strapping pins as outputs after boot is fine, which is why many boards put their LED on GPIO2.
  • Don't connect anything that pulls a strapping pin the "wrong" way at reset. Typical culprits: a sensor module with its own pull-up resistor on GPIO12, a button with an external pull-up on GPIO2, or a device that drives GPIO0 LOW while the board powers up.
  • GPIO0 can be a user button. The BOOT button already connects it to GND. Once your sketch is running, you can read it with pinMode(0, INPUT_PULLUP).

GPIO12 is the dangerous one. If you're stuck with a circuit that pulls it HIGH, the flash voltage can be fixed at 3.3 V permanently by burning an eFuse (espefuse.py set_flash_voltage 3.3V). That cannot be undone, so it's a last resort. Moving the connection to another pin is usually easier.

GPIO6–11: connected to the flash chip

Inside the module, GPIO6, 7, 8, 9, 10 and 11 connect to the SPI flash chip that holds your program. The CPU fetches code over these pins constantly, so using them for anything else crashes the chip. Some dev boards still bring them out to the headers, labelled CLK, SD0, SD1, SD2, SD3 and CMD. Treat those header pins as not there.

Boards with a WROVER module (which adds PSRAM) also use GPIO16 and GPIO17 for the PSRAM. Leave those alone on WROVER boards.

GPIO34–39: input only

GPIO34, 35, 36 and 39 can only read. GPIO36 and 39 are often labelled VP and VN (or SENSOR_VP and SENSOR_VN). GPIO37 and 38 exist on the chip but are not brought out on a WROOM-32.

These pins have no output driver and no internal pull-up or pull-down resistors. pinMode(34, INPUT_PULLUP) doesn't give you a pull-up: the pin stays floating. A button on one of these pins therefore needs an external resistor, for example 10 kΩ from the pin to 3.3 V with the button to GND. They are good pins for analog sensors (all four are ADC1 channels) and for signals that a sensor actively drives.

The ESP32 errata also notes that GPIO36 and GPIO39 can see brief glitches when the ADC or Wi-Fi powers up, so avoid them for edge-triggered interrupts.

ADC1 vs ADC2

The ESP32 has two analog-to-digital converters.

ADCGPIOs on a WROOM-32
ADC132, 33, 34, 35, 36, 39
ADC20, 2, 4, 12, 13, 14, 15, 25, 26, 27

The Wi-Fi driver uses ADC2. While Wi-Fi is running, analogRead() on an ADC2 pin doesn't return valid readings. The rule for any project that uses Wi-Fi: put analog inputs on ADC1 pins. Digital use of ADC2 pins (digitalRead, digitalWrite) is not affected.

analogRead() returns 0–4095 (12 bits) by default. The ADC isn't linear near 0 V or near the top of its range, so keep signals in the middle where you can. analogReadMilliVolts() applies the factory calibration stored in the chip, which makes readings more consistent between boards.

Touch pins

The ESP32 has ten capacitive touch channels, which detect a finger on a wire or a pad of copper tape.

Touch channelT0T1T2T3T4T5T6T7T8T9
GPIO40215131214273332

touchRead(4) returns a number that drops when the pad is touched on the original ESP32. Pick a threshold from readings you take yourself, because the values depend on pad size and wiring. T1, T2, T3 and T5 sit on strapping pins, so prefer T0, T4 and T6–T9.

Default I2C, SPI and UART pins in the Arduino core

The ESP32 can route most peripherals to almost any pin through its GPIO matrix, so these are defaults, not requirements:

  • I2C (Wire): SDA = GPIO21, SCL = GPIO22. Wire.begin() uses them, and Wire.begin(sda, scl) moves the bus to other output-capable pins (not 34–39).
  • SPI (SPI, the VSPI bus): SCK = GPIO18, MISO = GPIO19, MOSI = GPIO23, SS = GPIO5. The chip's other general-purpose SPI bus, HSPI, has native pins 14, 12, 13 and 15, which include two strapping pins. That's one reason VSPI is the default.
  • UART0 (Serial): TX = GPIO1, RX = GPIO3, wired to the USB-serial chip. Using them for something else breaks uploads and the Serial Monitor.
  • Extra UART (Serial2): pass the pins explicitly so you know where it is.
#include <Wire.h>
#include <SPI.h>
 
void setup() {
  Serial.begin(115200);
 
  Wire.begin(21, 22);                       // SDA, SCL (also the defaults)
  SPI.begin(18, 19, 23, 5);                 // SCK, MISO, MOSI, SS (VSPI defaults)
  Serial2.begin(9600, SERIAL_8N1, 16, 17);  // RX, TX - pick other pins on WROVER boards
 
  pinMode(34, INPUT);                       // input-only pin: INPUT_PULLUP would do nothing
  Serial.print("GPIO34 reads ");
  Serial.print(analogReadMilliVolts(34));
  Serial.println(" mV");
}
 
void loop() {
}

Next steps

Stuck on a step?

Describe your board and what you see; the assistant has this page as context.

Ask the assistant

Keep going