The default ESP32 SPI pinout uses the VSPI hardware bus, mapped to GPIO 18 (SCK), GPIO 19 (MISO), GPIO 23 (MOSI), and GPIO 5 (CS). If you are using the standard Arduino SPI.h library without explicitly defining pins, the firmware automatically routes your data through these exact GPIOs. The ESP32 actually features two usable hardware SPI buses for general peripherals (VSPI and HSPI), plus a third internal bus reserved strictly for the onboard flash memory.

The Master ESP32 SPI Pinout Table (VSPI & HSPI)

Below is the definitive GPIO mapping for the classic ESP32-WROOM-32 module. This table reflects the default Arduino Core mappings, which align with Espressif's ESP-IDF hardware SPI host definitions.

SPI Signal VSPI (Default / SPI2) HSPI (Secondary / SPI3) Direction (Master Perspective) Internal Pull-up?
SCK (Clock) GPIO 18 GPIO 14 Output No
MISO (Master In, Slave Out) GPIO 19 GPIO 12 Input No
MOSI (Master Out, Slave In) GPIO 23 GPIO 13 Output No
CS / SS (Chip Select) GPIO 5 GPIO 15 Output (Active LOW) Yes (Weak)
CRITICAL WARNING: The Forbidden SPI0/SPI1 Pins
Never attempt to use GPIO 6, 7, 8, 9, 10, or 11 for SPI peripherals. On all standard ESP32-WROOM and ESP32-WROVER modules, these pins are hardwired internally to the SPI flash and PSRAM. Connecting external sensors to these pins will cause immediate boot failures, random crashes, or permanent silicon damage.

Rows People Get Wrong (And How to Fix Them)

Even experienced makers stumble on specific SPI pinout assumptions. Here are the most common failure modes and how to resolve them on the bench.

1. The MISO / MOSI Swap

Because SPI is a master-slave (or controller-peripheral) protocol, 'Master In' means data flowing into the ESP32. A frequent mistake is wiring the MISO pin of a sensor to the MISO pin of the ESP32. Fix: Always wire MISO to MISO and MOSI to MOSI. The peripheral's MISO pin is an output; the ESP32's MISO pin (GPIO 19) is an input. If your SPI.transfer() returns 0x00 or 0xFF constantly, swap these two wires first.

2. Floating Chip Select (CS) Lines

Tutorials often omit the CS pin when demonstrating a single SPI device, relying on the peripheral's internal default state. In practice, a floating CS pin acts as an antenna, picking up EMI and causing the sensor to corrupt the SPI bus while other devices are talking. Fix: Always wire CS. If your ESP32 crashes when initializing a secondary SPI device, ensure the primary device's CS pin is explicitly driven HIGH via digitalWrite() or a 10kΩ physical pull-up resistor.

3. The 5V Logic Trap

The ESP32 operates at 3.3V logic. Many popular SPI displays and SD card modules (especially older Arduino-compatible ones) output 5V on their MISO line. Feeding 5V into GPIO 19 will degrade the ESP32's internal ESD protection diodes and eventually brick the pin. Fix: Use a bidirectional logic level shifter (like the TXB0108) or a simple BSS138 MOSFET-based shifter on all SPI lines if your peripheral lacks a dedicated 3.3V VCC jumper.

Faded Silkscreen? How to Verify Pins Safely

Cheap clone boards from overseas marketplaces frequently suffer from faded, misaligned, or entirely missing silkscreen pin markings. Guessing the pinout based on board color or USB chip type is a fast track to shorting 5V to a 3.3V GPIO.

When markings are illegible, use this bench procedure to safely map the SPI pins:

  1. Locate the Metal RF Shield: Look at the silver ESP32-WROOM module soldered to the board. The pin numbers are laser-etched into the metal shield or the green PCB substrate directly under the shield overhang.
  2. Set Your DMM to Continuity: Use a digital multimeter in continuity mode (looking for a < 1 Ω threshold).
  3. Trace from the Header to the Module: Place one probe on the ambiguous black header pin and the other on the corresponding exposed pad near the metal shield. A beep confirms the physical GPIO number.
  4. Cross-Reference the Datasheet: Once you identify the physical GPIO (e.g., GPIO 18), reference the official Espressif ESP32 Datasheet to confirm it is safe to use as an SPI output and isn't a strapping pin with conflicting boot requirements.
Strapping Pin Caveat: GPIO 12 (HSPI MISO) is a strapping pin. If it is pulled HIGH during boot, the ESP32 switches to 1.8V flash mode and will fail to boot. If you use HSPI, ensure GPIO 12 is pulled LOW or left floating during the power-on sequence.

Board Variants & Silicon Revisions

The physical layout of SPI pins changes depending on the development board variant and the specific silicon generation you are using. The table above applies to the classic ESP32 (WROOM/WROVER), but modern variants shift the native pins.

Board / Chip Variant Default SPI Bus SCK / MISO / MOSI / CS Notes & Gotchas
ESP32 DevKit v1 (30-pin) VSPI 18 / 19 / 23 / 5 Standard layout. Fits single breadboard.
ESP32 DevKit v1 (38-pin) VSPI 18 / 19 / 23 / 5 Wider layout. GPIO mapping identical to 30-pin, but physical headers won't fit a standard breadboard.
NodeMCU-32S VSPI 18 / 19 / 23 / 5 Silkscreen often labels these as SCK, MISO, MOSI explicitly. Highly reliable markings.
ESP32-S3 (Native SPI) SPI2 12 / 13 / 11 / 10 Completely different silicon architecture. VSPI/HSPI macros map differently. Use S3-specific pinouts.
ESP32-C3 SPI2 4 / 5 / 6 / 7 RISC-V architecture. Only one general-purpose hardware SPI bus available.

Decision Path: Which SPI Bus and Pins Should You Use?

Stop guessing which bus to initialize in your code. Follow this decision tree to select the exact hardware configuration for your project.

Project Condition Decision / Action Concrete Pick (Code & Wiring)
Using only one SPI device (e.g., a single TFT display or RFID reader). Use the default hardware bus. Do not define custom pins in code. VSPI: SCK=18, MISO=19, MOSI=23, CS=5.
Code: SPI.begin();
Using two SPI devices that need independent bus speeds (e.g., SD Card at 20MHz + Display at 40MHz). Use both hardware buses to avoid software multiplexing overhead. VSPI for Display, HSPI for SD Card.
Code: Initialize SPIClass vspi(VSPI); and SPIClass hspi(HSPI);
Using multiple SPI devices that share the same clock speed (e.g., three DACs). Share one hardware bus, but use individual Chip Select (CS) pins for each device. VSPI Shared: SCK=18, MISO=19, MOSI=23.
Assign unique CS pins (e.g., GPIO 5, 15, 4) for each peripheral.
Routing a custom PCB where default pins create trace crossover nightmares. Use ESP32's GPIO matrix to remap SPI to almost any available pin via software. Custom: Pick any GPIO (avoiding 6-11 and strapping pins).
Code: SPI.begin(sck, miso, mosi, cs);

The Default Recommendation: If you have no specific hardware routing constraints and are wiring up a quick prototype on a breadboard, always default to VSPI (GPIO 18, 19, 23, 5). It is the most universally supported bus across third-party Arduino libraries, requires zero configuration in your setup() loop, and avoids the strapping pin conflicts associated with HSPI's GPIO 12.

For deeper technical specifications on SPI clock dividers, DMA buffering, and interrupt handling, refer to the Espressif SPI Master API Documentation and the Arduino SPI Reference.