SPI is the fast bus. Where I2C is happy with a temperature sensor and a small OLED, SPI is what you reach for when you need to push pixels to a color TFT, read a microSD card, talk to a LoRa radio or an RFID reader. It uses more wires, but it moves data at megabits per second instead of kilobits.
The ESP32 is generous with SPI. It has two general-purpose SPI controllers, a flexible pin matrix and enough speed to drive a display smoothly. It is also a board where one of the default SPI pins is a boot strapping pin, which quietly causes one of the most confusing problems beginners hit with SD card modules. This guide covers the pins, the signals, a loopback test, an SD card example, using several devices together and the traps to avoid.
The Four SPI Signals
SPI uses four lines, and the names have changed over time, so you will see both versions in tutorials:
| Signal | Older name | Direction | Job |
|---|---|---|---|
| MOSI | Data out | ESP32 to device | Controller sends data |
| MISO | Data in | Device to ESP32 | Device sends data back |
| SCK | Clock | ESP32 to device | Sets the timing of every bit |
| CS (or SS) | Chip Select | ESP32 to device | Pull LOW to talk to that device |
Unlike I2C, SPI has no addresses. The ESP32 picks the device by pulling its CS line low. That means every extra device needs its own CS pin, while MOSI, MISO and SCK are shared by all of them.
SPI is full duplex: while the ESP32 sends a byte on MOSI, the device simultaneously sends a byte back on MISO. For a comparison with I2C, see I2C vs SPI.
ESP32 SPI Pins: VSPI and HSPI
The classic ESP32 has four SPI controllers. Two (SPI0 and SPI1) are used internally for the flash memory and are off limits. The two you can use are HSPI (SPI2) and VSPI (SPI3).
| Signal | VSPI (default SPI) | HSPI |
|---|---|---|
| MOSI | GPIO 23 | GPIO 13 |
| MISO | GPIO 19 | GPIO 12 |
| SCK | GPIO 18 | GPIO 14 |
| CS | GPIO 5 | GPIO 15 |
In Arduino code the default SPI object is VSPI. Whenever a library says "uses default SPI pins" on a classic ESP32, it means 23, 19, 18 and 5.
These pins are fastest when used as listed, because they connect through the chip's direct IO MUX (up to 80 MHz). You can move them to other pins through the GPIO matrix, but the maximum speed then drops to about 40 MHz, which is still very fast for hobby use.
Other chips differ. The ESP32-S3 and ESP32-C3 do not use the VSPI and HSPI names, and their default pins are different. If you copy pin numbers from a classic ESP32 guide onto an S3 or C3, check the chip's own pinout first (ESP32-S3, ESP32-C3).
Using the Default SPI Bus
#include <SPI.h>
const int CS_PIN = 5;
void setup() {
Serial.begin(115200);
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH); // deselect
SPI.begin(); // VSPI: SCK 18, MISO 19, MOSI 23
}
To choose your own pins, pass them in the order SCK, MISO, MOSI, CS:
SPI.begin(18, 19, 23, 5); // sck, miso, mosi, ss
To use the second controller (HSPI) alongside the first:
SPIClass hspi(HSPI);
void setup() {
hspi.begin(14, 12, 13, 15); // sck, miso, mosi, ss
}
Now you can run two completely independent buses, for example a display on VSPI and an SD card on HSPI, each at its own speed.
SPI Modes and Speed
Each SPI device expects a particular clock behavior, defined by two settings, clock polarity (CPOL) and clock phase (CPHA), giving four modes:
| Mode | CPOL | CPHA | Clock idles | Data sampled on |
|---|---|---|---|---|
| SPI_MODE0 | 0 | 0 | LOW | Rising edge |
| SPI_MODE1 | 0 | 1 | LOW | Falling edge |
| SPI_MODE2 | 1 | 0 | HIGH | Falling edge |
| SPI_MODE3 | 1 | 1 | HIGH | Rising edge |
The device datasheet tells you the mode. Mode 0 is by far the most common. Wrong mode symptoms are readings that are shifted, inverted, or just wrong.
You set the mode and speed for each transaction:
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // 1 MHz
digitalWrite(CS_PIN, LOW);
byte reply = SPI.transfer(0x90);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
Start slow, around 1 MHz, until everything works, then raise the speed. Long jumper wires limit how fast a bus can run reliably.
Test Without Any Device: The Loopback
You can prove your SPI pins work with a single jumper wire. Connect GPIO 23 (MOSI) to GPIO 19 (MISO). Whatever the ESP32 sends comes straight back.
#include <SPI.h>
const int CS_PIN = 5;
void setup() {
Serial.begin(115200);
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
SPI.begin(18, 19, 23, CS_PIN);
}
void loop() {
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW);
byte received = SPI.transfer(0xA5);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
Serial.printf("Sent 0xA5, received 0x%02X %s\n",
received, received == 0xA5 ? "OK" : "FAIL");
delay(1000);
}
You should see received 0xA5 OK. Remove the jumper and you will see 0xFF or 0x00, which proves the test is real. If the loopback passes, the ESP32 side is fine and any problem lives in your wiring or in the device.
Example: Reading and Writing a microSD Card
The microSD card module is the most popular SPI device for the ESP32.
| SD module pin | ESP32 pin |
|---|---|
| CS | GPIO 5 |
| SCK | GPIO 18 |
| MOSI (DI) | GPIO 23 |
| MISO (DO) | GPIO 19 |
| VCC | 5V (modules with an onboard regulator) or 3.3V (bare modules) |
| GND | GND |
Check which kind you have. The common blue module with a voltage regulator and level shifters wants 5V on VCC, and its logic outputs are safe for the ESP32. A bare microSD breakout with no regulator needs 3.3V.
Format the card as FAT32, then run:
#include <SPI.h>
#include <SD.h>
const int SD_CS = 5;
void setup() {
Serial.begin(115200);
if (!SD.begin(SD_CS)) {
Serial.println("SD card mount failed");
return;
}
File file = SD.open("/test.txt", FILE_WRITE);
if (file) {
file.println("Hello from the ESP32");
file.close();
Serial.println("Wrote /test.txt");
}
file = SD.open("/test.txt");
while (file && file.available()) {
Serial.write(file.read());
}
file.close();
Serial.printf("Card size: %llu MB\n", SD.cardSize() / (1024 * 1024));
}
void loop() {
}
If SD.begin() fails, check power (see the voltage note above), the card format and the wiring. Cards larger than 32 GB are often exFAT and may not mount; reformat them as FAT32.
Multiple SPI Devices on One Bus
All devices share MOSI, MISO and SCK. Each gets its own CS. Only one CS may be LOW at a time.
| Signal | Display | SD card |
|---|---|---|
| MOSI | GPIO 23 | GPIO 23 |
| MISO | GPIO 19 | GPIO 19 |
| SCK | GPIO 18 | GPIO 18 |
| CS | GPIO 15 | GPIO 5 |
const int TFT_CS = 15;
const int SD_CS = 5;
void setup() {
pinMode(TFT_CS, OUTPUT);
pinMode(SD_CS, OUTPUT);
digitalWrite(TFT_CS, HIGH); // deselect everything first
digitalWrite(SD_CS, HIGH);
SPI.begin(18, 19, 23);
// then call each library's begin() with its own CS pin
}
The most important habit: set every CS pin HIGH before initializing any device. A floating CS pin lets a device listen to traffic meant for another and corrupt the bus, causing the "it works alone but not together" problem.
Some devices (notably cheap SD modules) keep MISO driven even when not selected, which breaks the bus for other devices. If a display stops working when an SD module is attached, try putting the two on separate buses (VSPI and HSPI).
The Strapping Pin Trap
Two of the HSPI default pins are boot strapping pins:
- GPIO 12 (HSPI MISO) is read at power-up. If it is HIGH, the ESP32 selects the wrong flash voltage and will not boot.
- GPIO 15 (HSPI CS) is read at boot to control boot log output (less harmful).
- GPIO 5 (VSPI CS) is also a strapping pin but is normally harmless.
Here is how the trap springs: an SD card module on HSPI holds its MISO line high or lets it float high at power-up, and the board refuses to start even though the code is fine. Remove the module and the ESP32 boots normally, which makes people assume the board is faulty.
The safest approach is to use VSPI (GPIO 23, 19, 18, 5) for your main bus and avoid GPIO 12 for anything that can be HIGH at boot. If you need HSPI, move MISO to another free pin with hspi.begin(14, 27, 13, 15).
Troubleshooting SPI
Nothing happens at all. Run the loopback test. If it passes, check the device's power and CS wiring.
Data is shifted or garbage. Wrong SPI mode, or the clock is too fast for your wiring. Try SPI_MODE0 and a lower speed.
Works alone, fails with a second device. A CS pin is floating or not set HIGH before begin(). Initialize all CS pins HIGH first.
ESP32 will not boot with the module attached. The module is pulling GPIO 12 (or 0, 2, 15) the wrong way at boot. Move the pin.
SD card fails. Wrong VCC voltage for the module type, card not FAT32, or a loose wire. Try a smaller card first.
Display is slow. Raise the SPI clock to 40 MHz or 80 MHz, and use the default IO MUX pins.
Display library cannot find the pins. Libraries like TFT_eSPI use their own configuration file (User_Setup.h) that must list the exact SPI pins you wired.
Frequently Asked Questions
What are the default SPI pins on ESP32? MOSI 23, MISO 19, SCK 18 and CS 5 on VSPI, and MOSI 13, MISO 12, SCK 14 and CS 15 on HSPI.
Can I use any pins for SPI? Mostly yes, thanks to the GPIO matrix. Keep to the default pins when speed matters, and avoid flash pins (6 to 11) and input-only pins for outputs.
How many devices can share an SPI bus? As many as you have CS pins for. Electrical load and wire length limit the speed.
Can I use SPI and WiFi at the same time? Yes.
Is SPI faster than I2C? Much. SPI runs at megahertz speeds, I2C at 100 to 400 kHz.
Where to go next? See the UART guide for the third common bus, and the ESP32 pinout guide for the full pin map and where these pins sit on your board.