DEV Community

TinkerNews
TinkerNews

Posted on

The GPIO numbers that brick an ESP32 (before your code ever runs)

The GPIO numbers that brick an ESP32 (before your code ever runs)

The ESP32 has somewhere around thirty usable GPIOs depending on which package you bought, and roughly a dozen of them are traps. Some of them stop the board from booting at all. The rest only break in the field, on battery power, weeks after the firmware is flashed and you have gone home. Here is the list that has cost me the most weekends.

GPIO 6 through 11 are the flash bus. Those six pins run between the ESP32 and the SPI flash die sitting next to it on the module, and the ROM bootloader reads your firmware over them. Wire an SD card or a display to pins 9 and 10 and you get anything from a boot loop to a corrupted NVS partition. The cruel part is that the pins are still broken out on the WROOM headers, so they look free and people pick them. If you genuinely need more than twenty-odd pins, move to an ESP32-S3 or hang an I2C IO expander off two pins you trust.

Strapping pins are read at reset, before your code runs. GPIO 0, 2, 5, 12, and 15 are sampled by the ROM while EN rises. Your sketch can drive them freely afterwards, which is exactly why the bug hides: everything works on the bench where you power up with a button, and the unit fails the first time a supervisor chip or a battery switch does a cold start.

GPIO 0 low at reset drops the chip into the serial bootloader instead of your firmware. That is how flashing works, and it is also how a floating sensor line turns into a unit that never boots. GPIO 2 has to be low or floating for a download boot to work at all. GPIO 5 changes SDIO timing and rarely matters outside SDIO designs. GPIO 15 high silences the ROM bootloader log, which is great in production and miserable when a dead board is trying to tell you why over UART.

GPIO 12 is the trap that actually bricks things. The ROM calls it MTDI, and at reset it selects the flash I/O voltage: low means 3.3V, high means 1.8V. Your flash module runs at 3.3V. Hold GPIO 12 high during reset and the chip talks to flash at 1.8V levels, reads garbage, and reset-loops forever. Pull the offending resistor and the board wakes up again, which is the only comforting thing about this pin.

I built a battery-powered camera trigger for a friend's wildlife rig. It ran for two days on my desk, powered through the USB-UART adapter. He carried it out to the trail, connected the battery pack, and it never booted once. The villain was a 10k pull-up on GPIO 12 feeding the gate of a load-switch MOSFET, and that pull-up went to a rail which only existed on battery power. With the USB adapter attached, the rail sat at 0V and GPIO 12 read low at every reset. On battery it read high. Four hours of scope traces for a resistor that needed to live on GPIO 13. After that one, the full strapping table moved to a sheet of paper above the bench, and our ESP32 pinout chart is the same sheet if you want it.

Pins 34 to 39 are input only and have no pull-ups. Reads work. Writes silently do nothing. Put a button on GPIO 34 with INPUT_PULLUP and the pin floats, so you read phantom presses or a button that reports permanently closed when the air is humid enough. Use a real 10k to 3.3V and plain INPUT mode. Those pins are also the ADC1 channels, which is convenient until you remember the ESP32 ADC needs calibration before its numbers mean anything.

ADC2 disappears the moment WiFi starts. On the original ESP32, ADC2 covers GPIO 0, 2, 4, 12 through 15, and 25 through 27, and the WiFi subsystem shares the block. I put a soil moisture sensor on GPIO 25 in a greenhouse controller. Readings were perfect in setup(), the MQTT connection came up, and every read after that returned frozen or noisy garbage. If you need an analog channel and WiFi at the same time, stay on ADC1 (GPIO 32 through 39) or bolt on an external ADC. While you are deciding which signal lands on which pin, the calculators on our tools page will tell you what a divider draws all day and what your pull-ups should be, and that conversation happens before the PCB order instead of after it.

Almost every ESP32 field failure I have debugged came down to a pin doing something special at reset or at power-up. This is the second piece in a series about ESP32 mistakes we have paid for ourselves, and TinkerNews sends it out weekly (free, small but growing). Mark the strapping pins in red on the schematic, keep GPIO 6 through 11 empty, and give the input-only pins real resistors before the board goes to fab.

Top comments (0)