How to Find the Right PCB Assembly Factory in China: A Complete Guide
Searching for a PCB assembly factory in China can feel overwhelming. Thousands of factories in Shenzhen alone. Each one claiming to
SPI (Serial Peripheral Interface) is a high-speed, full-duplex synchronous serial protocol essential for connecting the ESP32 to a wide range of peripherals like displays, sensors, SD cards, and wireless modules. This comprehensive guide goes beyond basic theory to deliver practical, actionable knowledge on mastering the ESP32‘s SPI capabilities using the Arduino IDE. We’ll cover everything from the fundamentals of the four-wire bus to advanced techniques for using custom pins and managing multiple devices on independent SPI buses, all based on hands-on experience and the official ESP32 framework.

SPI operates on a master-slave architecture, where a single controller (the master, in this case, your ESP32) manages one or more peripheral devices (the slaves). It’s a synchronous protocol, meaning data transfer is coordinated by a clock signal (SCK) generated by the master. One of its key advantages is full-duplex communication; data can be sent from the master to the slave (via MOSI) and from the slave to the master (via MISO) simultaneously, making it faster than protocols like I2C.
To operate an SPI bus, you need four essential lines:
MOSI (Master Out Slave In): The line for data sent from the master to the slave.
MISO (Master In Slave Out): The line for data sent from the slave to the master.
SCK (Serial Clock): The clock signal generated by the master to synchronize data bits.
CS/SS (Chip Select / Slave Select): An active-low signal used by the master to select which specific slave device is active on the shared bus.
Pro-Tip: On slave devices like sensor modules, you might see the terms SDO (Serial Data Out) instead of MISO and SDI (Serial Data In) instead of MOSI. They refer to the same pins from the peripheral’s perspective.
The ESP32 chip is equipped with four SPI peripherals (SPI0, SPI1, HSPI, VSPI). However, SPI0 and SPI1 are dedicated to internal communication with the device’s flash memory and are not available for general use.
For your projects, you have two fully independent, user-configurable SPI buses:
HSPI (SPI2): The “High-Speed” SPI peripheral.
VSPI (SPI3): The “Very High-Speed” SPI peripheral (often used as the default).
Each of these buses (HSPI and VSPI) can control up to three slave devices by using separate Chip Select (CS) pins. This means you can natively connect up to six SPI devices to a single ESP32. For scenarios requiring more devices, an SPI multiplexer (mux) would be necessary.
Most common ESP32 development boards (like the ESP32 DevKit) come with a default pin mapping for the HSPI and VSPI buses. The following table shows the typical arrangement:
| SPI Bus | MOSI | MISO | SCK | CS (Example) |
|---|---|---|---|---|
| VSPI | GPIO 23 | GPIO 19 | GPIO 18 | GPIO 5 (Any GPIO) |
| HSPI | GPIO 13 | GPIO 12 | GPIO 14 | GPIO 15 (Any GPIO) |
⚠️ Critical Check: These are common defaults, but they can vary between board models and manufacturers (especially for ESP32-S3, which has a different mapping). Always verify your specific board’s pinout diagram.
You can also use this simple Arduino sketch to print your board’s configured default SPI pins to the Serial Monitor. Ensure you’ve selected the correct board under Tools > Board before uploading.
void setup() { Serial.begin(115200); Serial.print("Default MOSI: "); Serial.println(MOSI); Serial.print("Default MISO: "); Serial.println(MISO); Serial.print("Default SCK: "); Serial.println(SCK); Serial.print("Default SS: "); Serial.println(SS); } void loop() {}
A significant strength of the ESP32 is its GPIO matrix, which allows most peripheral functions to be mapped to almost any GPIO pin. You are not locked into the default SPI pins. There are two primary methods to use custom pins:
Most well-written peripheral libraries (e.g., for the BME280 sensor) allow you to specify the SPI pins when creating the device object. This is the cleanest and easiest method.
#include <Adafruit_BME280.h> #include <SPI.h> // Define your CUSTOM SPI pins (not the defaults) #define MY_MISO 32 #define MY_MOSI 26 #define MY_SCK 25 #define MY_CS 33 // Pass custom pins to the library constructor Adafruit_BME280 bme(MY_CS, MY_MOSI, MY_MISO, MY_SCK); void setup() { Serial.begin(9600); if (!bme.begin()) { Serial.println("Could not find BME280 sensor!"); while (1); } } // ... loop() to read sensor
If a library doesn’t offer a direct pin configuration, or you are writing low-level SPI code, you can initialize the bus with SPI.begin().
#include <SPI.h> #define CUSTOM_SCK 18 #define CUSTOM_MISO 19 #define CUSTOM_MOSI 23 #define CUSTOM_SS 5 void setup() { SPI.begin(CUSTOM_SCK, CUSTOM_MISO, CUSTOM_MOSI, CUSTOM_SS); // ... further device-specific setup }
You can connect several devices to the same SPI bus (e.g., VSPI) as long as each has a unique Chip Select (CS) pin. The master (ESP32) controls communication by pulling the CS pin of the target slave LOW and keeping all others HIGH.
#define CS_SENSOR_1 5 #define CS_SENSOR_2 17 void setup() { pinMode(CS_SENSOR_1, OUTPUT); pinMode(CS_SENSOR_2, OUTPUT); digitalWrite(CS_SENSOR_1, HIGH); // Deselect both initially digitalWrite(CS_SENSOR_2, HIGH); SPI.begin(); // Initialize the bus } void readSensor1() { digitalWrite(CS_SENSOR_1, LOW); // Select Sensor 1 // ... Perform SPI.transfer() commands here digitalWrite(CS_SENSOR_1, HIGH); // Deselect } void readSensor2() { digitalWrite(CS_SENSOR_2, LOW); // Select Sensor 2 // ... Perform SPI.transfer() commands here digitalWrite(CS_SENSOR_2, HIGH); // Deselect }
For higher performance or to isolate critical devices, you can use both HSPI and VSPI buses at the same time. This requires creating separate SPIClass objects.
#include <SPI.h> // Define pins for VSPI (can be default or custom) #define VSPI_MISO 19 #define VSPI_MOSI 23 #define VSPI_SCK 18 #define VSPI_SS 5 // Define pins for HSPI (can be default or custom) #define HSPI_MISO 12 #define HSPI_MOSI 13 #define HSPI_SCK 14 #define HSPI_SS 15 // Create two distinct SPI objects SPIClass * vspi = new SPIClass(VSPI); SPIClass * hspi = new SPIClass(HSPI); void setup() { // Initialize both buses with their respective pins vspi->begin(VSPI_SCK, VSPI_MISO, VSPI_MOSI, VSPI_SS); hspi->begin(HSPI_SCK, HSPI_MISO, HSPI_MOSI, HSPI_SS); pinMode(vspi->pinSS(), OUTPUT); // Set SS pins as outputs pinMode(hspi->pinSS(), OUTPUT); } void loop() { // Communicate with a device on the VSPI bus vspi->beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); digitalWrite(vspi->pinSS(), LOW); vspi->transfer(0xAA); // Example command digitalWrite(vspi->pinSS(), HIGH); vspi->endTransaction(); // Independently communicate with a device on the HSPI bus hspi->beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); digitalWrite(hspi->pinSS(), LOW); hspi->transfer(0xBB); // Example command digitalWrite(hspi->pinSS(), HIGH); hspi->endTransaction(); delay(1000); }
Let’s solidify the concepts with a practical example of reading from a BME280 temperature, pressure, and humidity sensor via SPI.
/* * ESP32 SPI Communication with BME280 Sensor * Uses custom SPI pins and demonstrates library integration. */ #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <SPI.h> // 1. DEFINE CUSTOM SPI PINS #define BME280_SCK 25 #define BME280_MISO 32 #define BME280_MOSI 26 #define BME280_CS 33 // 2. CREATE SENSOR OBJECT WITH CUSTOM PINS Adafruit_BME280 bme(BME280_CS, BME280_MOSI, BME280_MISO, BME280_SCK); void setup() { Serial.begin(115200); Serial.println(F("BME280 SPI Test")); // 3. INITIALIZE SENSOR if (!bme.begin()) { Serial.println("Failed to find BME280. Check wiring/pins!"); while (1); } } void loop() { // 4. READ AND PRINT VALUES Serial.print("Temperature = "); Serial.print(bme.readTemperature()); Serial.println(" °C"); Serial.print("Pressure = "); Serial.print(bme.readPressure() / 100.0F); // Convert to hPa Serial.println(" hPa"); Serial.print("Humidity = "); Serial.print(bme.readHumidity()); Serial.println(" %"); Serial.println("----------------"); delay(5000); // Wait 5 seconds }
| Problem | Likely Cause | Solution |
|---|---|---|
| No response from slave | Incorrect wiring, wrong CS pin, or slave not powered. | Double-check MISO/MOSI/SCK/CS and GND connections. Verify the slave is powered (3.3V). Manually toggle the CS pin LOW/HIGH. |
| Garbage/incorrect data | SPI mode mismatch or excessive clock speed. | Ensure the master and slave use the same SPI Mode (MODE0, MODE1, MODE2, MODE3). Reduce the SPI clock speed (SPISettings). |
| SPI bus conflicts | Multiple slaves interfering on the same CS line. | Ensure only one CS pin is LOW at any time. Add a small delay between selecting different slaves. |
begin() fails with custom pins |
Pins are not suitable for output or are already in use. | Avoid using strapping pins (GPIO 0, 2, 12, 15 on boot). Check your board’s pinout for restricted pins. |
This guide provides the foundational and advanced knowledge to confidently implement SPI communication in your ESP32 projects. By understanding the dual-bus architecture, mastering custom pin configuration, and following best practices for multi-slave management, you can interface with a vast ecosystem of high-speed peripherals.
======================================
Searching for a PCB assembly factory in China can feel overwhelming. Thousands of factories in Shenzhen alone. Each one claiming to
ESP32s.com – Your Local Partner in China’s Electronics Hub “I walk the floor so you don’t have to. Here is
The world of AI is buzzing. You have likely heard of OpenClaw, the open-source AI agent that has exploded on GitHub,
If you manufacture electronics—whether IoT devices, consumer gadgets, medical instruments, or industrial controls—you already know that China’s Pearl River Delta (PRD) is
If you’re sourcing electronics from China, you’ve likely faced the same challenges: unreliable suppliers, quality inconsistencies, communication gaps, and the
If you’re searching for a low-cost, all-in-one touchscreen solution for your next IoT or human-machine interface (HMI) project, you’ve likely
No account yet?
Create an Account