There's something very nice about ESP32 Wroom modules: you can use fixtures to quickly debug things, remove a module, solder it and viola, you have something working. Or a bad board. Mostly those options. ====== Introduction ====== I've been using a fixture by [[http://www.diymalls.com/|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 ([[https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf|datasheet]]). {{:tamiwiki:users:6r1d:signal-2025-10-18-123252_002.jpeg?400|}} {{:tamiwiki:users:6r1d:signal-2025-10-18-123334_002.jpeg?400|}} 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, [[https://aliexpress.com/item/1005008525778779.html|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. ====== Pin assignments ====== ^ 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 | ===== Notes ===== * GPIO pin 48 is the onboard LED, usually blue. * Initially, I thought that onboard USB-UART is connected to something else than RXD0 / TXD0. It was not. It is the main UART. ====== Tracing code: Arduino ====== 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 // ====== CONFIG ====== // Blink timing (change at runtime with: delay ) 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 -> start blinking that GPIO")); Serial.println(F(" -> same as 'pin '")); Serial.println(F(" delay -> 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 ' to blink, 'delay ' 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); } } ====== Feedback ====== If you’d like to suggest corrections or aren’t satisfied with how I wrote this short article, feel free to ping me on [[https://t.me/I_am_6r1d|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.