ESP32 Pins: Strapping Pins, Input-Only, Boot Traps
Reset-Time Ownership, Module Constraints, and Pin Assignments That Survive Layout
The robot hand team has outgrown jumper wires. They are making a small controller PCB around an ESP32 module. The firmware already works on a dev board, so the first draft of the schematic feels easy: copy the GPIO numbers from the prototype and route them to sensors, buttons, LEDs, motor enables and a debug connector.
Then the first board arrives. It programs once, boots sometimes, and refuses to start when a limit switch is held. The bug is not in the application. It happened before the application had a chance to run.
An ESP32 pin is not just a numbered solder pad. It may be a boot-mode input during reset, a flash-memory bus line inside the module, a UART console pin during download, an ADC input during measurement, a wake source during sleep, or an ordinary GPIO only after all of that is finished. The craft is to decide who owns the pin at each moment.
By the end, you can
- Separate a microcontroller GPIO number from the module pad and the development-board header
- Explain why strapping pins can change boot mode before firmware starts
- Avoid flash-bus pads, console pins and boot pins unless the schematic explicitly allows the use
- Use GPIO34-39 as inputs only, with external bias when the signal needs a defined idle state
- Build a pin-assignment table that records reset state, runtime function, sleep behavior and fallback debug access
- Power-cycle a design in worst-case external states to prove it still boots
The pin number is the last thing you choose
When a schematic says GPIO25, it is tempting to treat that as a free software
constant. In reality, the physical path has layers:
- The silicon pad inside the ESP32.
- The module routing from silicon to package edge.
- Any flash, antenna, crystal or RF constraints inside the module.
- The carrier board's USB bridge, buttons, LEDs, pull resistors and headers.
- The product circuit attached to the pin.
The exact list changes across ESP32, ESP32-S2, ESP32-S3, ESP32-C3 and board variants. This lesson uses the classic ESP32-WROOM family as the mental model because it is common and full of teachable traps. For a product, the final authority is the module datasheet and the development board or carrier-board schematic.
The module view matters because several pins are not available in the way a bare microcontroller table suggests. On common ESP32-WROOM modules, the SPI flash is already connected. Pins used by that flash bus are not a place for your status LED, button or sensor. They may appear in a datasheet table, but the module has already spent them.
You copied a working ESP32 dev-board prototype to a custom PCB. Which document should settle whether a GPIO is truly available?
-
Correct. Availability is a physical-board question, not just a software-name question.
-
The same GPIO number can be routed, strapped, reserved or missing depending on the module and board.
-
Pinout images are useful prompts, but they often omit board-specific pullups, LEDs and strap behavior.
-
Code compilation proves nothing about reset-time ownership or electrical loading.
Reset time belongs to the boot ROM
Before your firmware starts, the ESP32 has to decide how to boot. It samples a small set of strapping pins during reset release. Those sampled levels tell the chip things such as normal boot versus serial download mode and, on older module combinations, flash-voltage selection.
This is why a button on the wrong pin can make a board fail only in a very specific human state. A bench test that power-cycles the board with no buttons held might pass. The same board can fail in the product when a user holds a button while inserting a battery, or when a sensor output is low during a cold start.
GPIO0 is the most famous example. On many ESP32 boards, pulling GPIO0 low during reset selects the serial bootloader. That is useful when you intentionally press a BOOT button to program the chip. It is a trap when a product button or external circuit can hold the same pin low during power-up.
GPIO12 is the scarier classic trap. On many ESP32 modules, its reset-time level can affect flash-voltage configuration. A board that accidentally pulls it high can look dead because the CPU is trying to boot from flash under the wrong electrical condition.
A product button pulls GPIO0 to ground when pressed. The firmware plans to use the
button only after boot. What failure can happen if the user holds the button while
inserting the battery?
Show worked solution
The chip can sample GPIO0 low at reset and enter serial download mode instead of
normal firmware. The fix is not a firmware delay; the reset-time circuit must be
changed or the button moved to a pin that is not a boot strap.
Input-only pins are not almost-outputs
GPIO34 through GPIO39 on classic ESP32 are useful, but they are only inputs. They cannot drive an LED, MOSFET gate, enable pin, chip select or bus line. They also do not provide the internal pull-up and pull-down resistors many beginner examples rely on.
This distinction is easy to miss because a firmware API may let you write code that looks symmetrical:
pinMode(34, INPUT) feels like the same kind of statement as pinMode(25, OUTPUT).
Electrically, they are different promises. GPIO34 can observe. GPIO25 can observe and drive. If your schematic needs a line to source or sink current, do not assign it to an input-only pad.
Input-only pins are still excellent for the right jobs: battery voltage sense through a divider, an analog sensor output, a wake input with an external pull, or a read-only fault line from another device. They become bad pins when the schematic secretly asks them to drive.
Use a pin planner before layout
The planner below is not a substitute for a datasheet. It is a habit-building filter: choose a pin, choose the intended job, state the reset pull, and mark whether the external circuit can drive the net while reset is active.
review result
A reasonable first choice after checking the exact board schematic.
Try these moves:
- Choose
GPIO25as an output. It should look like a reasonable first choice. - Change the pin to
GPIO34while keeping the use as output or PWM. The result should become a hard rejection. - Choose
GPIO0, set the reset pull to down, and mark the external circuit active at reset. That is the bootloader trap. - Choose
GPIO12, set the reset pull to up, and mark the external circuit active at reset. That is the flash-voltage trap. - Choose
GPIO34as analog with an external pull. That is a reasonable read-only use.
Which pin assignment deserves the strongest objection?
-
Correct. GPIO34 is input-only, so it cannot drive PWM.
-
GPIO21 is a common I2C SDA choice on many classic ESP32 boards. You still check the exact schematic.
-
GPIO35 is input-only, but that can be fine for an analog sense input when the analog design is checked.
-
Using the documented console pins for debug UART is normal when programming and boot logs are accounted for.
The assignment table is part of the design
For a serious board, the pin assignment table should exist before the PCB is routed. It is not paperwork. It is a way to catch conflicts while they are still cheap.
For each signal, record:
- GPIO number and physical module pad.
- Runtime function: input, output, ADC, I2C, SPI, UART, PWM, wake source.
- Reset state: floating, pulled up, pulled down, driven by another IC, or connected to a button.
- Boot role: strap, console, flash bus, ordinary GPIO, or variant-specific special function.
- Sleep role: wake source, retained state, high impedance, or powered-off external circuit.
- Fallback plan: how the board can still be programmed and debugged if firmware is broken.
The fallback plan matters more than it sounds. If you spend UART0 pins on unrelated signals and remove the auto-program circuit, a board can become hard to recover after one bad firmware image. If you put a high-current load on a strap pin, the boot state can depend on whether an external device is powered first. If you choose input-only pins for a bus, the compiler may not save you before the PCB is manufactured.
A practical ESP32 pin-review loop
Read the module datasheet, the board schematic, and the silicon technical reference in that order. Mark flash pins and strap pins in red. Mark console/programming pins in a different color. Reserve clean general-purpose pins for signals that must drive loads. Then run the physical test: power-cycle the actual board while every external signal is forced into its worst reset-time state.
Key takeaways
- A GPIO assignment is a timing question: reset, boot ROM, firmware, sleep and debug can all care about the same pad.
- Strapping pins are sampled before your code runs, so firmware cannot fix a bad reset-time circuit.
- GPIO34-39 are useful input and ADC pins, but they cannot drive and they need external bias when a defined idle state matters.
- Flash-bus pins and console pins may appear in tables, but the module or board may already own them.
- A pin-assignment table and worst-case power-cycle test are cheaper than a PCB respin.
The lesson is not "never use special pins." Engineers use special pins all the time. The lesson is to stop pretending all pin numbers are equal. A good ESP32 schematic knows which pins are safe, which pins are conditional, and which pins are already spoken for before the first line of user firmware executes.