ESP32 SPI Communication: Set Pins, Multiple SPI Bus Interfaces, and Peripherals (Arduino IDE)
Learn about SPI communication protocol with the ESP32 using Arduino IDE. We’ll take a look at the ESP32 SPI pins, how to connect SPI devices, define custom SPI pins, how to use multiple SPI devices, and much more.
- Suitable forB.Tech/Diploma IoT/ESP32 students
- Build time1-2 weeks
- LevelBeginner
- Available atAjvika Labs, Kakinada
About this project
This is a simple guide about SPI communication protocol with the ESP32 using Arduino IDE. We’ll take a look at the ESP32 SPI pins, how to connect SPI devices, define custom SPI pins, how to use multiple SPI devices, and much more.
Table of Contents:
Introducing ESP32 SPI Communication Protocol
ESP32 SPI Peripherals
ESP32 SPI Pins
Using Custom ESP32 SPI Pins
ESP32 with Multiple SPI Devices
Multiple SPI Devices (same bus, different CS pin)
Using Two SPI Bus Interfaces (use HSPI and VSPI simultaneously)
This tutorial focus on programming the ESP32 using the Arduino core, so before proceeding, you should have the ESP32 add-on installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.
Installing the ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux instructions)
Alternatively, you can also use VS Code with the PlatformIO extension to program your boards using the Arduino core:
Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)
Introducing ESP32 SPI Communication Protocol
SPI stands for
S
erial
P
eripheral
I
nterface, and it is a synchronous serial data protocol used by microcontrollers to communicate with one or more peripherals. For example, your ESP32 board communicating with a sensor that supports SPI or with another microcontroller.
In an SPI communication, there is always a
controller
(also called
master
) that controls the
peripheral
devices (also called
slaves
). Data can be sent and received simultaneously. This means that the master can send data to a slave, and a slave can send data to the master at the same time.
You can have
only one master
, which will be a microcontroller (the ESP32), but you can have multiple slaves. A slave can be a sensor, a display, a microSD card, etc., or another microcontroller. This means you can have an ESP32 connected to multiple sensors, but the same sensor can’t be connected to multiple ESP32 boards simultaneously.
SPI Interface
For SPI communication you need four lines:
MISO
: Master In Slave Out
MOSI
: Master Out Slave In
SCK
: Serial Clock
CS
/
SS
: Chip Select (used to select the device when multiple peripherals are used on the same SPI bus)
On a slave-only device, like sensors, displays, and others, you may find a different terminology:
MISO
may be labeled as
SDO
(Serial Data Out)
MOSI
may be labeled as
SDI
(Serial Data In)
ESP32 SPI Peripherals
The ESP32 integrates 4 SPI peripherals: SPI0, SPI1, SPI2 (commonly referred to as
HSPI
), and SPI3 (commonly referred to as
VSPI
).
SP0 and SP1 are used internally to communicate with the built-in flash memory, and you should not use them for other tasks.
You can use
HSPI
and
VSPI
to communicate with other devices. HSPI and VSPI have independent bus signals, and each bus can drive up to three SPI slaves.
ESP32 Default SPI Pins
Many ESP32 boards come with default SPI pins pre-assigned. The pin mapping for most boards is as follows (
it is different for an ESP32-S3
):
SPI
MOSI
MISO
SCLK
CS
VSPI
GPIO 23
GPIO 19
GPIO 18
GPIO 5
HSPI
GPIO 13
GPIO 12
GPIO 14
GPIO 15
Warning:
depending on the board you’re using, the default SPI pins might be different. So, make sure you check the pinout for the board you’re using. Additionally, some boards don’t have pre-assigned SPI pins, so you need to set them on code.
Note:
usually, when not specified, the board will use the VSPI pins when initializing an SPI communication with the default settings.
Whether your board comes with pre-assigned pins or not, you can always set them on code.
Finding your ESP32 Board’s Default SPI Pins
If you’re not sure about your board’s default SPI pins, you can upload the following code to find out.
/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
//Find the default SPI pins for your board
//Make sure you have the right board selected in Tools > Boards
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print(\"MOSI: \");
Serial.println(MOSI);
Serial.print(\"MISO: \");
Serial.println(MISO);
Serial.print(\"SCK: \");
Serial.println(SCK);
Serial.print(\"SS: \");
Serial.println(SS);
}
void loop() {
// put your main code here, to run repeatedly:
}
Important:
make sure you select the board you’re using in
Tools
>
Board
, otherwise, you may not get the right pins.
After uploading the code, open the Serial Monitor, RST your board and you’ll see the SPI pins.
Using Custom ESP32 SPI Pins
When using libraries to interface with your SPI peripherals, it’s usually simple to use custom SPI pins because you can pass them as arguments to the library constructor.
For example, take a quick look at the following example that interfaces with a
BME280 sensor
using the
Adafruit_BME280
library.
/*
Based on the Adafruit_BME280_Library example: https://github.com/adafruit/Adafruit_BME280_Library/blob/master/examples/bme280test/bme280test.ino
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include `Wire.h`
#include `Adafruit_Sensor.h`
#include `Adafruit_BME280.h`
#include `SPI.h`
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
#define SEALEVELPRESSURE_HPA (1013.25)
//Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
void setup() {
Serial.begin(9600);
Serial.println(F(\"BME280 test\"));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
if (!status) {
Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");
while (1);
}
Serial.println(\"-- Default Test --\");
delayTime = 1000;
Serial.println();
}
void loop() {
printValues();
delay(delayTime);
}
void printValues() {
Serial.print(\"Temperature = \");
Serial.print(bme.readTemperature());
Serial.println(\" *C\");
// Convert temperature to Fahrenheit
/*Serial.print(\"Temperature = \");
Serial.print(1.8 * bme.readTemperature() + 32);
Serial.println(\" *F\");*/
Serial.print(\"Pressure = \");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(\" hPa\");
Serial.print(\"Approx. Altitude = \");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(\" m\");
Serial.print(\"Humidity = \");
Serial.print(bme.readHumidity());
Serial.println(\" %\");
Serial.println();
}
You can easily pass your custom SPI pins to the library constructor.
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
In that case, I was using the following SPI pins (not default) and everything worked as expected:
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
If you’re not using a library, or the library you’re using doesn’t accept the pins in the library constructor, you may need to initialize the SPI bus yourself. In that case, you would need to call the
SPI.begin()
method on the
setup()
and pass the SPI pins as arguments:
SPI.begin(SCK, MISO, MOSI, SS);
You can see an example of this scenario
in this tutorial
, in which we
Table of Contents:
Introducing ESP32 SPI Communication Protocol
ESP32 SPI Peripherals
ESP32 SPI Pins
Using Custom ESP32 SPI Pins
ESP32 with Multiple SPI Devices
Multiple SPI Devices (same bus, different CS pin)
Using Two SPI Bus Interfaces (use HSPI and VSPI simultaneously)
This tutorial focus on programming the ESP32 using the Arduino core, so before proceeding, you should have the ESP32 add-on installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.
Installing the ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux instructions)
Alternatively, you can also use VS Code with the PlatformIO extension to program your boards using the Arduino core:
Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)
Introducing ESP32 SPI Communication Protocol
SPI stands for
S
erial
P
eripheral
I
nterface, and it is a synchronous serial data protocol used by microcontrollers to communicate with one or more peripherals. For example, your ESP32 board communicating with a sensor that supports SPI or with another microcontroller.
In an SPI communication, there is always a
controller
(also called
master
) that controls the
peripheral
devices (also called
slaves
). Data can be sent and received simultaneously. This means that the master can send data to a slave, and a slave can send data to the master at the same time.
You can have
only one master
, which will be a microcontroller (the ESP32), but you can have multiple slaves. A slave can be a sensor, a display, a microSD card, etc., or another microcontroller. This means you can have an ESP32 connected to multiple sensors, but the same sensor can’t be connected to multiple ESP32 boards simultaneously.
SPI Interface
For SPI communication you need four lines:
MISO
: Master In Slave Out
MOSI
: Master Out Slave In
SCK
: Serial Clock
CS
/
SS
: Chip Select (used to select the device when multiple peripherals are used on the same SPI bus)
On a slave-only device, like sensors, displays, and others, you may find a different terminology:
MISO
may be labeled as
SDO
(Serial Data Out)
MOSI
may be labeled as
SDI
(Serial Data In)
ESP32 SPI Peripherals
The ESP32 integrates 4 SPI peripherals: SPI0, SPI1, SPI2 (commonly referred to as
HSPI
), and SPI3 (commonly referred to as
VSPI
).
SP0 and SP1 are used internally to communicate with the built-in flash memory, and you should not use them for other tasks.
You can use
HSPI
and
VSPI
to communicate with other devices. HSPI and VSPI have independent bus signals, and each bus can drive up to three SPI slaves.
ESP32 Default SPI Pins
Many ESP32 boards come with default SPI pins pre-assigned. The pin mapping for most boards is as follows (
it is different for an ESP32-S3
):
SPI
MOSI
MISO
SCLK
CS
VSPI
GPIO 23
GPIO 19
GPIO 18
GPIO 5
HSPI
GPIO 13
GPIO 12
GPIO 14
GPIO 15
Warning:
depending on the board you’re using, the default SPI pins might be different. So, make sure you check the pinout for the board you’re using. Additionally, some boards don’t have pre-assigned SPI pins, so you need to set them on code.
Note:
usually, when not specified, the board will use the VSPI pins when initializing an SPI communication with the default settings.
Whether your board comes with pre-assigned pins or not, you can always set them on code.
Finding your ESP32 Board’s Default SPI Pins
If you’re not sure about your board’s default SPI pins, you can upload the following code to find out.
/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
//Find the default SPI pins for your board
//Make sure you have the right board selected in Tools > Boards
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print(\"MOSI: \");
Serial.println(MOSI);
Serial.print(\"MISO: \");
Serial.println(MISO);
Serial.print(\"SCK: \");
Serial.println(SCK);
Serial.print(\"SS: \");
Serial.println(SS);
}
void loop() {
// put your main code here, to run repeatedly:
}
Important:
make sure you select the board you’re using in
Tools
>
Board
, otherwise, you may not get the right pins.
After uploading the code, open the Serial Monitor, RST your board and you’ll see the SPI pins.
Using Custom ESP32 SPI Pins
When using libraries to interface with your SPI peripherals, it’s usually simple to use custom SPI pins because you can pass them as arguments to the library constructor.
For example, take a quick look at the following example that interfaces with a
BME280 sensor
using the
Adafruit_BME280
library.
/*
Based on the Adafruit_BME280_Library example: https://github.com/adafruit/Adafruit_BME280_Library/blob/master/examples/bme280test/bme280test.ino
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include `Wire.h`
#include `Adafruit_Sensor.h`
#include `Adafruit_BME280.h`
#include `SPI.h`
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
#define SEALEVELPRESSURE_HPA (1013.25)
//Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
void setup() {
Serial.begin(9600);
Serial.println(F(\"BME280 test\"));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
if (!status) {
Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");
while (1);
}
Serial.println(\"-- Default Test --\");
delayTime = 1000;
Serial.println();
}
void loop() {
printValues();
delay(delayTime);
}
void printValues() {
Serial.print(\"Temperature = \");
Serial.print(bme.readTemperature());
Serial.println(\" *C\");
// Convert temperature to Fahrenheit
/*Serial.print(\"Temperature = \");
Serial.print(1.8 * bme.readTemperature() + 32);
Serial.println(\" *F\");*/
Serial.print(\"Pressure = \");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(\" hPa\");
Serial.print(\"Approx. Altitude = \");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(\" m\");
Serial.print(\"Humidity = \");
Serial.print(bme.readHumidity());
Serial.println(\" %\");
Serial.println();
}
You can easily pass your custom SPI pins to the library constructor.
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
In that case, I was using the following SPI pins (not default) and everything worked as expected:
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
If you’re not using a library, or the library you’re using doesn’t accept the pins in the library constructor, you may need to initialize the SPI bus yourself. In that case, you would need to call the
SPI.begin()
method on the
setup()
and pass the SPI pins as arguments:
SPI.begin(SCK, MISO, MOSI, SS);
You can see an example of this scenario
in this tutorial
, in which we
Technologies used
Hardware components
- ESP32
- ESP8266
- Arduino
- BME280
- SD Card
Software & tools
- Arduino IDE
- VS Code
- PlatformIO
- PlatformIO
What you get
- Working project / hardware kit, tested before delivery
- Complete source code with explanation
- Project demo and guidance for your viva
- Project report, PPT and IEEE paper support (on request)
Delivery & Payment
Delivery takes a minimum of two weeks from the date your inquiry is confirmed.
Payment is in two parts:
• 50% advance to start the project
• Remaining 50% before delivery
Note:
• Documentation and PPT are not included by default
• If you need documentation/PPT, it can be arranged for extra time and cost
We'll confirm the exact timeline and cost with you after reviewing your requirements.