This is a straightforward overview concerning SPI interaction protocol with the ESP32 making use of Arduino IDE. We’ll have a look at the ESP32 SPI pins, just how to link SPI tools, specify personalized SPI pins, how to make use of several SPI gadgets, and far more.
Table of Contents:
This tutorial emphasis on configuring the ESP32 making use of the Arduino core, so prior to continuing, you must have the ESP32 add-on set up in your Arduino IDE. Comply with the following tutorial to set up the ESP32 on the Arduino IDE, if you have not currently.
Alternatively, you can also utilize VS Code with the PlatformIO extension to configure your boards using the Arduino core:
Introducing ESP32 SPI Communication Protocol
SPI represents Serial Peripheral Interface, and also it is a simultaneous serial information method utilized by microcontrollers to communicate with several peripherals. Your ESP32 board communicating with a sensing unit that sustains SPI or with one more microcontroller.
In an SPI communication, there is always a controller (likewise called master) that controls the peripheral gadgets (additionally called slaves). Data can be sent and also obtained all at once. This indicates that the master can send data to a servant, as well as a servant can send data to the master at the very same time.
You can have just one master, which will be a microcontroller (the ESP32), yet you can have several servants. A slave can be a sensor, a display screen, a microSD card, and so on, or an additional microcontroller. This implies you can have an ESP32 connected to multiple sensors, yet the same sensor can’t be connected to multiple ESP32 boards all at once.
SPI Interface
For SPI interaction you require 4 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 displays, others, as well as sensing units, you might discover 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 (typically described as HSPI), and SPI3 (generally referred to as VSPI).
SP0 and SP1 are made use of inside to interact with the built-in flash memory, as well as you should not utilize them for other tasks.
You can utilize HSPI and also VSPI to connect with other tools. HSPI as well as VSPI have independent bus signals, and each bus can increase to 3 SPI servants.
ESP32 Default SPI Pins
Many ESP32 boards include default SPI pins pre-assigned. The pin mapping for a lot of boards is as adheres to:
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 could be different. Make certain you check the pinout for the board you’re utilizing. In addition, some boards do not have actually pre-assigned SPI pins, so you require to set them on code.
Note: generally, when not specified, the board will make use of the VSPI pins when initializing an SPI communication with the default setups.
Whether your board features pre-assigned pins or not, you can constantly set them on code.
Finding your ESP32 Board’s Default SPI Pins
If you’re not certain concerning your board’s default SPI pins, you can publish the adhering to code to figure out.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-spi-communication-arduino/
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 certain you pick the board you’re making use of in Tools > > Board, otherwise, you may not obtain the right pins.
After submitting the code, open the Serial Monitor, RST your board and you’ll see the SPI pins.
Using Custom ESP32 SPI Pins
When making use of libraries to user interface with your SPI peripherals, it’s usually simple to utilize personalized SPI pins because you can pass them as arguments to the library constructor.
For instance, take a peek at the following example that interfaces with an utilizing the Adafruit_BME280 library.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-spi-communication-arduino/
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 manufacturer.
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
In that case, I was making use of the complying with SPI pins (not default) as well as everything worked as anticipated:
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
If you’re not utilizing a library, or the collection you’re making use of does not approve the pins in the collection fitter, you might require to boot up the SPI bus yourself. In that situation, you would certainly need to call the SPI.begin() approach on the configuration() and pass the SPI pins as debates:
SPI.begin(SCK, MISO, MOSI, SS);
You can see an instance of this situation, in which we boot up an SPI LoRa transceiver that is connected to personalized SPI pins. revealing just how to.
ESP32 with Multiple SPI Devices
As we’ve seen previously, you can make use of two various SPI buses on the ESP32 and also each bus can attach approximately 3 various peripherals. This suggests that we can connect approximately six SPI gadgets to the ESP32. You can use an SPI multiplexer if you require to make use of much more.
Multiple SPI Devices (same bus, different CS pin)
To attach multiple SPI devices, you can use the exact same SPI bus as long as each outer uses a various CS pin.
To select the outer you want to communicate with, you ought to establish its CS pin to LOW. Picture you have outer 1 and peripheral 2. To read from outer 1, make certain its CS pin is set to LOW (here represented as CS_1):
digitalWrite(CS_1, LOW); // enable CS pin to read from peripheral 1
/*
use any SPI functions to communicate with peripheral 1
*/
Then, at exact same point, you’ll wish to check out from outer 2. You need to disable outer 1 CS pin by establishing it to HIGH, and enable outer 2 CS pin by setting it to LOW:
digitalWrite(CS_1, HIGH); // disable CS pin from peripheral 1
digitalWrite(CS_2, LOW); // enable CS pin to read from peripheral 2
/*
use any SPI functions to communicate with peripheral 2
*/
ESP32 Using Two SPI Bus Interfaces (Use HSPI and VSPI simultaneously)
To interact with several SPI peripherals concurrently, you can utilize the ESP32 2 SPI buses (HSPI and VSPI). You can utilize the default HSPI and VSPI pins or make use of personalized pins.
Briefly, to utilize HSPI and also VSPI concurrently, you just require to.
1) First, make certain you consist of the SPI collection in your code.
#include <SPI.h>
2) Initialize 2 SPIClass things with various names, one on the HSPI bus and also one more on the VSPI bus. :
vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
3) Call the begin() approach on those things.
vspi.begin();
hspi.begin();
You can pass personalized pins to the begin() technique if needed.
vspi.begin(VSPI_CLK, VSPI_MISO, VSPI_MOSI, VSPI_SS);
hspi.begin(HSPI_CLK, HSPI_MISO, HSPI_MOSI, HSPI_SS);
4) Finally, you likewise need to establish the SS pins as outcomes. :
pinMode(VSPI_SS, OUTPUT);
pinMode(HSPI_SS, OUTPUT);
Then, utilize the typical commands to connect with the SPI tools, whether you’re using a sensing unit library or the SPI collection techniques.
You can discover an instance of exactly how to utilize several SPI buses on the. See the instance listed below:
/* The ESP32 has four SPi buses, however as of right now only two of
* them are available to use, HSPI and VSPI. Simply using the SPI API
* as illustrated in Arduino examples will use VSPI, leaving HSPI unused.
*
* However if we simply intialise two instance of the SPI class for both
* of these buses both can be used. However when just using these the Arduino
* way only will actually be outputting at a time.
*
* Logic analyser capture is in the same folder as this example as
* "multiple_bus_output.png"
*
* created 30/04/2018 by Alistair Symonds
*/
#include <SPI.h>
// Define ALTERNATE_PINS to use non-standard GPIO pins for SPI bus
#ifdef ALTERNATE_PINS
#define VSPI_MISO 2
#define VSPI_MOSI 4
#define VSPI_SCLK 0
#define VSPI_SS 33
#define HSPI_MISO 26
#define HSPI_MOSI 27
#define HSPI_SCLK 25
#define HSPI_SS 32
#else
#define VSPI_MISO MISO
#define VSPI_MOSI MOSI
#define VSPI_SCLK SCK
#define VSPI_SS SS
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
#endif
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define VSPI FSPI
#endif
static const int spiClk = 1000000; // 1 MHz
//uninitalised pointers to SPI objects
SPIClass * vspi = NULL;
SPIClass * hspi = NULL;
void setup() {
//initialise two instances of the SPIClass attached to VSPI and HSPI respectively
vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
//clock miso mosi ss
#ifndef ALTERNATE_PINS
//initialise vspi with default pins
//SCLK = 18, MISO = 19, MOSI = 23, SS = 5
vspi->begin();
#else
//alternatively route through GPIO pins of your choice
vspi->begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_SS); //SCLK, MISO, MOSI, SS
#endif
#ifndef ALTERNATE_PINS
//initialise hspi with default pins
//SCLK = 14, MISO = 12, MOSI = 13, SS = 15
hspi->begin();
#else
//alternatively route through GPIO pins
hspi->begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_SS); //SCLK, MISO, MOSI, SS
#endif
//set up slave select pins as outputs as the Arduino API
//doesn't handle automatically pulling SS low
pinMode(vspi->pinSS(), OUTPUT); //VSPI SS
pinMode(hspi->pinSS(), OUTPUT); //HSPI SS
}
// the loop function runs over and over again until power down or reset
void loop() {
//use the SPI buses
spiCommand(vspi, 0b01010101); // junk data to illustrate usage
spiCommand(hspi, 0b11001100);
delay(100);
}
void spiCommand(SPIClass *spi, byte data) {
//use it as you would the regular arduino SPI API
spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(spi->pinSS(), LOW); //pull SS slow to prep other end for transfer
spi->transfer(data);
digitalWrite(spi->pinSS(), HIGH); //pull ss high to signify end of data transfer
spi->endTransaction();
}
Wrapping Up
This article was a fast as well as basic guide revealing you just how to utilize SPI communication with the ESP32 using the Arduino core– with the ESP32 serving as a controller (master).
In summary, the ESP32 has four SPI buses, yet only two can be utilized to manage peripherals, the HSPI as well as VSPI. Many ESP32 have actually pre-assigned HSPI and VSPI GPIOs, yet you can constantly transform the pin task in the code.
You can utilize the HSPI and also VSPI buses simultaneously to drive numerous SPI peripherals, or you can use several peripherals on the exact same bus as long as their CS pin is attached to a various GPIO.
We didn’t dive deeply into examples, because each case, sensing unit, and collection circumstance is different. Now you must have a much better suggestion of how to user interface one or multiple SPI gadgets with the ESP32.
For even more thorough information about the SPI Master driver on the ESP32, you can.
We didn’t cover establishing the ESP32 as an SPI slave, but you can.