1. Today's topic
ESP32/ESP32-C3 GPIO constraints and pin-selection rules: strapping pins, UART0, Flash/PSRAM pins, JTAG/SWD, input-only GPIO, ADC2 with Wi-Fi and board_pins.h organisation. The main idea: in embedded systems, GPIO cannot be selected merely because a pin is “free”. Each pin has a history: boot, Flash, debugging, analogue constraints, pull resistors, sleep/wakeup and peripheral functions.
2. Why this matters
Important project uses:
- UART2 for EC25: TX17/RX16 on ESP32-WROOM-32U;
- UART0 for CLI/flashing;
- traffic-light inputs;
- GPIO for EC25 PWRKEY/RESET;
- GPIO for camera/radar/Jetson power;
- 1-Wire DS18B20;
- PPS/20 Hz synchronisation signals;
- HIL through Raspberry Pi;
- porting to STM32.
Incorrect GPIO choices produce strange symptoms:
- The board sometimes fails to start;
- firmware cannot be flashed;
- ESP32 enters download mode;
- UART logging is silent;
- Wi-Fi breaks ADC operation;
- JTAG/SWD does not work;
- an external input changes the boot mode.
3. Strapping pins
Strapping pins are pins the microcontroller reads at reset/startup to select boot parameters. For the classic ESP32:
GPIO0
GPIO2
GPIO5
GPIO12 / MTDI
GPIO15 / MTDOFor ESP32-C3:
GPIO2
GPIO8
GPIO9After startup, they may work as ordinary GPIO, but their level at reset has already affected boot.
4. GPIO0
GPIO0 = LOW at reset -> serial bootloader / download mode
GPIO0 = HIGH at reset -> normal execution from flashPractical conclusion: do not casually connect GPIO0 to an external signal that may be LOW at power-up. If an optocoupler input is connected to GPIO0, ESP32 may enter the bootloader instead of running the firmware.
5. GPIO2
For flashing, GPIO2 must be in a correct state. It is better not to use it for an input that may be strongly pulled to the wrong level at reset.
6. GPIO12 / MTDI
GPIO12 is particularly risky: it is related to selection of the Flash VDD_SDIO voltage. If GPIO12 is high at reset, ESP32 may select an unsuitable Flash voltage. Practical conclusion: avoid GPIO12 for external signals that may be HIGH at startup.
7. GPIO15 / MTDO
GPIO15 may affect boot logging. If it is in the wrong state, UART logging may appear silent.
8. Flash/PSRAM pins
For ESP32:
GPIO6-GPIO11 - normally SPI flash, do not use
GPIO16/GPIO17 - may be occupied by PSRAM on some modulesOn ESP32-WROOM-32U without PSRAM, GPIO16/GPIO17 are often available, so UART2 TX17/RX16 may be acceptable. Recheck when changing to a WROVER/PSRAM module. On ESP32-C3, GPIO12-GPIO17 are often associated with Flash.
9. UART0
GPIO1/GPIO3 are normally used for flashing/debugging. Recommended policy:
UART0:
flashing, monitor, CLI, HIL-debug
UART2:
EC25 modem
other GPIO:
inputs, power control, synchronisation signals10. Input-only GPIO34-GPIO39
On ESP32, GPIO34-GPIO39 are input-only. They have no software-enabled pull-up/pull-down. Suitable for:
- Discrete inputs;
- ADC;
- status signals.
Unsuitable for:
- Relay control;
- EC25 PWRKEY/RESET;
- LED;
- 1-Wire;
- I2C;
- lines requiring an internal pull resistor.
11. ADC2 and Wi-Fi
On ESP32, ADC2 conflicts with Wi-Fi. If Wi-Fi is used, it is better to plan analogue measurements on ADC1. Initial policy:
analogue measurements + Wi-Fi -> ADC1
use ADC2 only with a full understanding of the restriction12. The STM32 parallel: preserve SWD
STM32 has a similar issue with debug pins:
PA13 / SWDIO
PA14 / SWCLK
NRST
BOOT0Recommendations:
- Do not allocate PA13/PA14 at an early stage;
- bring NRST out to a connector;
- do not leave BOOT0 floating;
- keep the SWD connector accessible.
13. Common mistakes
- Connecting an external input to GPIO0;
- using GPIO12 as an ordinary input with an external pull-up;
- allocating UART0 to operational peripherals;
- using GPIO34-GPIO39 as outputs;
- relying on an internal pull resistor for an important signal;
- using ADC2 together with Wi-Fi;
- disabling STM32 SWD in CubeMX.
14. Practical task
Create PIN_AUDIT.md:
# Pin audit
## Target MCU/module
- MCU: ESP32-WROOM-32U
- Framework: ESP-IDF 6.x
- Board revision: ...
## Reserved / dangerous pins
### ESP32 strapping pins
- GPIO0: BOOT / download mode
- GPIO2: boot mode related
- GPIO5: strapping
- GPIO12 / MTDI: flash voltage strap
- GPIO15 / MTDO: boot log strap
Rule:
Do not connect external uncontrolled signals to strapping pins.
### Flash / PSRAM
- GPIO6-GPIO11: do not use
- GPIO16/GPIO17: check exact module
### UART0
- GPIO1 TX0: console/flashing/debug
- GPIO3 RX0: console/flashing/debug
### Input-only
- GPIO34-GPIO39: input only, no internal pull-up/down
### ADC
- Prefer ADC1 when Wi-Fi is used.
- Avoid ADC2 for measurements that must work during Wi-Fi operation.A table:
| Signal | GPIO | Direction | Boot-sensitive? | Pull | Owner component | Risk | Decision |
|---|---:|---|---|---|---|---|---|
| EC25_TXD | 17 | ESP32 TX | check module | external/device | modem_service | PSRAM conflict on some modules | OK for WROOM-32U |
| EC25_RXD | 16 | ESP32 RX | check module | external/device | modem_service | PSRAM conflict on some modules | OK for WROOM-32U |
| CLI_TXD | 1 | TX | UART0 | USB-UART | cli_service | boot/debug conflict | keep |
| CLI_RXD | 3 | RX | UART0 | USB-UART | cli_service | boot/debug conflict | keep |15. A short recap
Design pin assignments before code. A basic ESP32 policy:
UART0 GPIO1/GPIO3:
reserve for flashing, CLI, monitor, HIL recovery.
GPIO0/GPIO2/GPIO5/GPIO12/GPIO15:
do not use for external signals with uncertain levels.
GPIO6-GPIO11:
do not use, flash.
GPIO34-GPIO39:
input only, external pulls are required when needed.
ADC:
with Wi-Fi, prefer ADC1.
EC25 UART:
UART2 TX17/RX16 is acceptable for the current ESP32-WROOM-32U,
but recheck when changing the module.Exercise
A UART2 assignment uses TX17/RX16 on ESP32-WROOM-32U without PSRAM. The design changes to a PSRAM module. List the assumptions that must be rechecked before reusing the old board_pins.h.
Self-check criteria: Name module-specific memory reservations and boot/debug checks; do not transfer the old pin map blindly.
Show the supplied answer
Recheck the exact module’s Flash/PSRAM pin reservations, whether GPIO16/GPIO17 are available, boot/strapping behaviour and debugging/flashing access. An assignment acceptable for the old module is not automatically acceptable for the new one. Record the selected module and external reset-time levels in the pin audit.