This is an old revision of the document!
There’s something great about ESP32-WROOM modules: you can prototype fast, use test fixtures to debug, then pop a module off, solder it onto a board, and—voilà—you’ve got a working device. Or a non-working one. Usually it's one of those two.
I've been using a fixture by DIYMall for some time at this point. From my perspective, its main feature is the support for ESP32-S3-WROOM-1U and ESP32-S3-WROOM-1 (datasheet).
It is very helpful as a fixture. It is not helpful whatsoever in terms of having a proper documentation. If you open the DIYMalls site, it shows 403 and doesn't give you anything.
So, the fixture is easy to buy on AliExpress, for example, here, it works without hassle, and it will also require you trace all pins unless you have a reference.
Thus, I've traced everything I found so you don't have to.
| ESP32-S3-Wroom contact pad | Datasheet reference | Arduino pin | Board pin |
|---|---|---|---|
| 39 | IO1 | 1 | D36 |
| 38 | IO2 | 2 | D35 |
| 4 | IO4 | 4 | D1 |
| 5 | IO5 | 5 | D2 |
| 6 | IO6 | 6 | D3 |
| 7 | IO7 | 7 | D4 |
| 12 | IO8 | 8 | D9 |
| 17 | IO9 | 9 | D14 |
| 18 | IO10 | 10 | D15 |
| 19 | IO11 | 11 | D16 |
| 20 | IO12 | 12 | D17 |
| 21 | IO13 | 13 | D18 |
| 22 | IO14 | 14 | D19 |
| 8 | IO15 | 15 | D5 |
| 9 | IO16 | 16 | D6 |
| 10 | IO17 | 17 | D7 |
| 11 | IO18 | 18 | D8 |
| 23 | IO21 | 21 | D20 |
| 31 | IO38 | 38 | D28 |
| 32 | IO39 | 39 | D29 |
| 33 | IO40 | 40 | D30 |
| 34 | IO41 | 41 | D31 |
| 35 | IO42 | 42 | D32 |
| 24 | IO47 | 47 | D21 |
| 25 | IO48 | 48 | D22 |
| 36 | RXD0 | 36 | D33 |
| 37 | TXD0 | 37 | D34 |
You may disagree with me and want to run a quick check. For your convenience, there's a code to do so. I didn't bother to write it manually, but I have tested it and it worked for me.
Flash from Arduino IDE. Use the ESP32 board package by Espressif, pick ESP32S3 Dev Module, pick 115200 baud in Serial Monitor.
#include <Arduino.h> // ====== CONFIG ====== // Blink timing (change at runtime with: delay <ms>) static uint32_t BLINK_DELAY_MS = 100; // Conservative "test-safe GPIO" set for ESP32-S3 modules. // Excludes pins that commonly serve boot strapping, USB D-/D+, PSRAM/flash, UART0 console, or JTAG, // which can interfere with programming, boot, or board-level functions during quick tests. // // Notes (summarized from the ESP32-S3 module/chip docs): // - 0, 3, 45, 46 are strapping/boot-related or JTAG-related; driving them can change boot modes or disable debug. // - 19, 20 are the on-chip USB D-/D+ differential pair; toggling them breaks USB comms. // - 35, 36, 37 are wired to Octal PSRAM on some variants; not available as GPIO there. // - 43 (U0TXD), 44 (U0RXD) are UART0 console pins used by Serial; poking them disrupts logs/flashing. // - 47, 48 form a differential SPI clock pair; safe as GPIO but mind 1.8 V I/O level on some R16V variants. const uint8_t TEST_SAFE_GPIO[] = { 1, 2, // ADC/Touch-capable, OK for digital use 4, 5, 6, 7, // " 8, 9, 10, 11, 12, 13, 14, // " 15, 16, 17, 18, // " // 19,20 excluded (USB D-/D+) 21, // plain GPIO // 22..34 don't exist on S3 modules // 35,36,37 excluded (PSRAM on some variants) 38, 39, 40, 41, 42, // JTAG-capable if configured, but OK as GPIO when JTAG is not in use // 43,44 excluded (UART0 TX/RX used by Serial) 47, 48 // differential clock pair; OK as GPIO (watch 1.8 V I/O on some R16V variants) }; const size_t TEST_SAFE_GPIO_COUNT = sizeof(TEST_SAFE_GPIO) / sizeof(TEST_SAFE_GPIO[0]); // ====== RUNTIME STATE ====== static int currentPin = -1; static bool blinking = false; // ====== HELPERS ====== bool isTestSafe(int gpio) { for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { if ((int)TEST_SAFE_GPIO[i] == gpio) return true; } return false; } void printTestSafePins() { Serial.println(F("\nTest-safe GPIOs (ESP32-S3):")); for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { Serial.print(F(i ? ", " : " ")); Serial.print(TEST_SAFE_GPIO[i]); } Serial.println(); Serial.println(F("Excluded (reason): 0,3,45,46 (boot/strap/JTAG) | 19,20 (USB D-/D+) | 35-37 (PSRAM on some) | 43,44 (UART0 console)")); } void stopBlink() { if (currentPin >= 0) { digitalWrite(currentPin, LOW); pinMode(currentPin, INPUT); // leave safe } blinking = false; currentPin = -1; Serial.println(F("Stopped. Pin released to INPUT.")); } void startBlink(int gpio) { if (!isTestSafe(gpio)) { Serial.print(F("Restricted: GPIO ")); Serial.print(gpio); Serial.println(F(" is not in the test-safe set.")); return; } if (blinking && currentPin == gpio) { Serial.print(F("Already blinking GPIO ")); Serial.println(gpio); return; } // switch pin if needed if (blinking && currentPin != gpio) stopBlink(); currentPin = gpio; pinMode(currentPin, OUTPUT); digitalWrite(currentPin, LOW); blinking = true; Serial.print(F("Blinking GPIO ")); Serial.print(currentPin); Serial.print(F(" at ")); Serial.print(BLINK_DELAY_MS); Serial.println(F(" ms.")); } // Parse commands like: // list // pin 10 // delay 250 // stop void handleCommand(String line) { line.trim(); if (line.length() == 0) return; line.toLowerCase(); if (line == "list") { printTestSafePins(); return; } if (line == "stop") { stopBlink(); return; } if (line.startsWith("pin ")) { int gpio = line.substring(4).toInt(); startBlink(gpio); return; } if (line.startsWith("delay ")) { int v = line.substring(6).toInt(); if (v < 10) v = 10; // clamp a bit BLINK_DELAY_MS = (uint32_t)v; Serial.print(F("Set blink delay to ")); Serial.print(BLINK_DELAY_MS); Serial.println(F(" ms.")); return; } // Single-number shortcut: just type the GPIO number bool allDigits = true; for (size_t i = 0; i < (size_t)line.length(); ++i) { if (!isDigit(line[i])) { allDigits = false; break; } } if (allDigits) { startBlink(line.toInt()); return; } Serial.println(F("Commands:")); Serial.println(F(" list -> show test-safe GPIOs")); Serial.println(F(" pin <gpio> -> start blinking that GPIO")); Serial.println(F(" <gpio> -> same as 'pin <gpio>'")); Serial.println(F(" delay <ms> -> set blink delay")); Serial.println(F(" stop -> stop and release pin")); } void setup() { Serial.begin(115200); // give USB-Serial/JTAG a moment delay(400); Serial.println(F("\nESP32-S3 GPIO Quick Tester")); Serial.println(F("Type 'list' to see test-safe pins, 'pin <gpio>' to blink, 'delay <ms>' to change speed, 'stop' to release.\n")); printTestSafePins(); } void loop() { // Process serial line input static String line; while (Serial.available()) { char c = (char)Serial.read(); if (c == '\r') continue; if (c == '\n') { handleCommand(line); line = ""; } else { line += c; } } // Blink the selected pin if (blinking && currentPin >= 0) { digitalWrite(currentPin, HIGH); delay(BLINK_DELAY_MS); digitalWrite(currentPin, LOW); delay(BLINK_DELAY_MS); } else { delay(5); } }
If you’d like to suggest corrections or aren’t satisfied with how I wrote this short article, feel free to ping me on Telegram.
This topic might deserve to be expanded into a document outside of my personal page. Then again, I’m not sure how many people around here actually use ESP32-S3.