How to use SPI (Serial Peripheral Interface) in Arduino to Communication between two Arduino Boards

Published  December 18, 2018   3
How to use SPI in Arduino: Communication between two Arduino Boards

A Microcontroller uses many different protocols to communicate with various sensors and modules. There are many different types of communication protocols for wireless and wired communication, and the most commonly used communication technique is Serial Communication. Serial communication is the process of sending data one bit at a time, sequentially, over a communication channel or bus. There are many types of serial communication, like UART, CAN, USB, I2C, and SPI communication. This complete guide explains Arduino SPI communication between two Arduino boards, including Arduino SPI pinout diagrams, wiring examples, and complete code examples.

Regardless of whether you are developing sensor networks or multi-device projects, understanding SPI connection Arduino techniques will greatly enhance your microcontroller projects.

In this tutorial, we learn about the SPI protocol and how to use it in Arduino. We will use the SPI Protocol for communication between two Arduinos. Here, one Arduino will act as the Master and another one will act as the Slave; two LEDs and push buttons will be connected to both Arduinos. To demonstrate SPI communication, we will control the master side LED with the push button at the slave side and vice versa using the SPI Serial communication protocol.

Important Note: A new resolution is underway to improve the terminology used in SPI communication by removing office words like "Master" and "Slave" while discussing SPI communication. According to this new resolution, people are encouraged to use the word "Controller" in place of "Master" and "Peripheral" in place of "Slave". It is expected that the terms MOSI/MISO and SS will be changed to SDI(Serial Data In) /SDO(Serial Data Out)and CS(Chip Select), respectively. For the sake of avoiding confusion, we have still used the old terminology in the article, but we encourage our readers to practise the new terms. 

What is Arduino SPI Communication?

SPI (Serial Peripheral Interface) is a serial communication protocol. The SPI interface was found by Motorola in 1970. SPI has a full-duplex connection, which means that the data is sent and received simultaneously. That is, a master can send data to a slave and a slave can send data to the master simultaneously. SPI is synchronous serial communication means the clock is required for communication purposes.

SPI communication is previously explained in other microcontrollers:

Arduino SPI Communication Important Features:

» Full-duplex - Allows simultaneous data transfer
» High transfer speed - Data rates up to 8MHz clock speed
» Multiple slave support - A single master can handle multiple slaves
» Synchronous protocol - Clock signal synchronises data transfer
» Short communication distance - Perfect for PCB level replacement 

How Arduino SPI Communication Works?

An SPI has a master/Slave communication by using four lines. An SPI can have only one master and can have multiple slaves. A master is usually a microcontroller, and the slaves can be a microcontroller, sensors, ADC, DAC, LCD, etc. The Arduino SPI communication diagram shows a master-slave architecture using four essential signals. Understanding this Arduino SPI pinout is crucial for successful implementation.

Below is the block diagram representation of the SPI Master with a Single Slave. Basic Arduino to Arduino SPI communication setup involves one master controlling one slave device.

SPI communication circuit between a master and slave

SPI has the following four lines: MISO, MOSI, SS, and CLK

  • MISO (Master in Slave Out) - The Slave line for sending data to the master.
  • MOSI (Master Out Slave In) - The Master line for sending data to the peripherals.
  • SCK (Serial Clock) - The clock pulses that synchronise data transmission are generated by the master.
  • SS (Slave Select) –Master can use this pin to enable and disable specific devices.

SPI Master with Multiple Slaves:  Advanced SPI connection Arduino setup allows one master to control multiple slave devices using individual SS pins.

Arduino UNO SPI pinout diagram showing MOSI, MISO, SCK, and SS pin locations

To start communication between master and slave, we need to set the required device's Slave Select (SS) pin to LOW, so that it can communicate with the master. When it's high, it ignores the master. This allows you to have multiple SPI devices sharing the same MISO, MOSI, and CLK lines of the master. As you can see in the above image, there are four slaves in which the SCLK, MISO, and MOSI are commonly connected to the master, and the SS of each slave is connected separately to individual SS pins (SS1, SS2, SS3) of the master. By setting the required SS pin LOW, a master can communicate with that slave.

Arduino SPI Pin Configuration - Quick Reference

The image below shows the SPI pins present on Arduino UNO (in red box). The Arduino SPI pinout varies between different Arduino models. Here's the comprehensive pinout guide for popular Arduino boards:

 

SPI Pins in Arduino UNO

 

SPI SignalArduino UNO PinAlternative PinFunction
MOSI11ICSP-4Master Output, Slave Input
MISO12ICSP-1Master Input, Slave Output
SCK13ICSP-3Serial Clock
SS10Any Digital PinSlave Select

 

Using SPI Protocol in Arduino

Before starting programming for SPI communication between two Arduinos. We need to learn about the Arduino SPI library used in the Arduino IDE.

The library <SPI.h> is included in the program to use the following functions for SPI communication. 

 

1. SPI.begin()

USE: To initialise the SPI bus by setting SCK, MOSI, and SS to outputs, pulling SCK and MOSI low, and SS high.

 

2. SPI.setClockDivider(divider)

USE: To set the SPI clock divider relative to the system clock. The available dividers are 2, 4, 8, 16, 32, 64 or 128.

Dividers: 

  • SPI_CLOCK_DIV2
  • SPI_CLOCK_DIV4
  • SPI_CLOCK_DIV8
  • SPI_CLOCK_DIV16
  • SPI_CLOCK_DIV32
  • SPI_CLOCK_DIV64
  • SPI_CLOCK_DIV128

 

3. SPI.attachInterrupt(handler)

USE: This function is called when a slave device receives data from the master.

 

4. SPI.transfer(val)

USE: This function is used to simultaneously send and receive data between the master and slave.

 

So now let’s start with a practical demonstration of the SPI protocol in Arduino. In this tutorial, we will use two Arduinos, one as the master and the other as the slave. Both Arduinos are attached with an LED & a push button separately. Master LED can be controlled by using the slave Arduino’s push button, and the slave Arduino’s LED can be controlled by the master Arduino’s push button using the SPI communication protocol present in Arduino.

 

Components Required for Arduino SPI Communication Project

  • Arduino UNO (2)
  • LED (2)
  • Push Button (2)
  • Resistor 10k (2)
  • Resistor 2.2k (2)
  • Breadboard
  • Connecting Wires

 

Arduino SPI Communication Circuit Diagram

The circuit diagram below shows how to use SPI on Arduino UNO, but you can follow the same procedure for Arduino Mega SPI Communication or Arduino nano SPI Communication. Almost everything will remain the same except for the PIN. You have to check the pinout of Arduino Nano or Arduino Mega to find the Arduino Nano SPI pins and Arduino Mega pins. Once you have done that, everything else will be the same. 

SPI Communication between Two Arduino Circuit Diagram

I have built the above-shown circuit over a breadboard. You can see the circuit set-up that I used for testing below. 

Communication Between Two Arduino using SPI Protocol

 

How to Program Arduino for SPI Communication: 

This tutorial has two programs, one for the master Arduino and the other for the slave Arduino. Complete programs for both sides are given at the end of this project.

 

Arduino SPI Communication Programming

This detailed Arduino SPI communication diagram shows the complete wiring for SPI communication between two Arduino boards with LED and button controls.

 

1. First of all, we need to include the SPI library for using SPI communication functions.

#include<SPI.h>                             

 

2. In void setup()

  • We Start Serial Communication at a Baud Rate of 115200.
Serial.begin(115200);    

           

  • Attach the LED to pin 7 and the push button to pin 2, and set those pins to OUTPUT and INPUT, respectively.
pinMode(ipbutton,INPUT);                
pinMode(LED,OUTPUT);           

 

  • Next, we begin the SPI communication
SPI.begin();  

                         

  • Next, we set the Clockdivider for SPI communication. Here we have set the divider to 8.
SPI.setClockDivider(SPI_CLOCK_DIV8);    

 

  • Then set the SS pin HIGH since we didn’t start any transfer to the slave Arduino.
digitalWrite(SS,HIGH);     

 

3. In void loop():

  • We read the status of the pushbutton pin connected to pin 2 (Master Arduino) for sending those values to the slave Arduino.
buttonvalue = digitalRead(ipbutton);   

 

  • Set Logic for Setting x value (To be sent to slave) depending upon input from pin 2
  if(buttonvalue == HIGH)               
  {
    x = 1;
  }
  else
  {
    x = 0;
  }
  • Before sending the value, we need to LOW the slave select value to begin the transfer to the slave from the master.
digitalWrite(SS, LOW);         

          

  • Here comes the important step. In the following statement, we send the push button value stored in the MasterSend variable to the slave Arduino and also receive a value from the slave that will be stored in the MasterReceive variable.
Mastereceive=SPI.transfer(Mastersend); 

 

  • After that, depending upon the Mastereceive value, we will turn the Master Arduino LED ON or OFF.
  if(Mastereceive == 1)                   
  {
    digitalWrite(LED,HIGH);        //Sets pin 7 HIGH
    Serial.println("Master LED ON");
  }
  else
  {
   digitalWrite(LED,LOW);         //Sets pin 7 LOW
   Serial.println("Master LED OFF");
  }

 

Note: We use Serial.println() to view the result in the Serial Motor of Arduino IDE. Check the Video at the end.

 

Arduino SPI Slave Programming Explanation

1. First of all, we need to include the SPI library for using SPI communication functions. Programming Arduino SPI communication requires separate code for master and slave devices. This section provides complete code examples with detailed explanations.

#include<SPI.h>  

                          

2. In void setup()

  • We Start Serial Communication at a Baud Rate of 115200.
Serial.begin(115200);    

           

  • Attach the LED to pin 7 and the push button to pin 2, and set those pins to OUTPUT and INPUT, respectively.
pinMode(ipbutton,INPUT);           
pinMode(LED,OUTPUT);      

   

  • The important step here is the following statements
pinMode(MISO,OUTPUT);                  

The above statement sets MISO as OUTPUT (Have to send data to Master IN). So data is sent via MISO of the Slave Arduino.

  • Now, turn on SPI in Slave Mode by using the SPI Control Register
SPCR |= _BV(SPE);         

          

  • Then turn ON the interrupt for SPI communication. If data is received from the master, the Interrupt Routine is called, and the received value is taken from SPDR (SPI data Register)
SPI.attachInterrupt();         

   

  • The value from the master is taken from SPDR and stored in the Slavereceived variable. This takes place in the following Interrupt Routine function.
ISR (SPI_STC_vect)
{
  Slavereceived = SPDR;                  
  received = true;                       
}

 

3. Next in void loop (), we set the Slave Arduino LED to turn ON or OFF depending upon the Slavereceived value.

  if (Slavereceived==1)
   {
   digitalWrite(LEDpin,HIGH); //Sets pin 7 as HIGH LED ON
                                     Serial.println("Slave LED ON");
     }
else
      {
  digitalWrite(LEDpin,LOW);     //Sets pin 7 as LOW LED OFF
  Serial.println("Slave LED OFF");
      }

 

  • Next, we read the status of the Slave Arduino Push button and store the value in Slavesend to send the value to the Master Arduino by giving a value to the SPDR register.
  buttonvalue = digitalRead(buttonpin); 
  if (buttonvalue == HIGH)              
      {
        x=1;
      }
else
      {
       x=0;
      }
            Slavesend=x;                             
            SPDR = Slavesend;  

 

Note: We use Serial.println() to view the result in the Serial Motor of Arduino IDE. Check the Video at the end.

 

Testing Arduino SPI Communication - Let's test it!

Below is the picture of the final setup for SPI communication between two Arduino Boards. 

When the push button on the Master side is pressed, the white LED on the slave side turns ON.

 

And when the push button on the Slave side is pressed, the Red LED at the Master side turns ON.

Testing SPI Communication between Two Arduinos

 

Advanced Arduino SPI Communication Applications

∗ Sensor Networks: Chain multiple sensors using SPI for high sampling rate data acquisition systems.
∗ Display Interfaces: Drive TFT displays, OLED screens, and LCD modules via SPI for graphical applications.
∗ Memory Devices: Interface with SD cards, EEPROM, and Flash memory via SPI devices.
∗ Wireless Modules: Control NRF24L01, ESP8266, and other wireless communication modules via SPI.

Technical Summary and GitHub Repository 

The Technical Summary presents a quick overview of the project, including its main idea, its operational concept and implementation details. It is designed to give readers a rapid understanding of the project without having to lose themselves in extensive documentation. Our GitHub Repository provides complete source code, schematics, and other resources for the practical use of this information.

code and schematicsDownload Zip

The detailed guide walks you through the real coding based on the theory of Arduino SPI communication between two devices. With the knowledge of the Arduino SPI pinout and the Arduino SPI connection methods, it becomes easier to create projects with multiple devices. This demonstrated SPI communication between two Arduino boards serves as a basic function for more complicated projects such as sensor arrays, display drivers, and radio systems.

This practical demonstration shows SPI communication between two Arduino boards working effectively with cross-controlled LEDs. You can check out the video below to see the demonstration of Arduino SPI communication. If you have any questions, please leave them in the comment section or use our forum

 

Frequently Asked Questions - Arduino SPI Communication

 ⇥ 1. Which Arduino SPI pins are used?
Arduino UNO employs pins 11 (MOSI), 12 (MISO), 13 (SCK), and 10 (SS) for SPI communication. Alternative connections are provided on the ICSP header using these same pins.

 ⇥ 2. How many devices can you connect to Arduino SPI?
One Arduino master can control multiple SPI slave devices by using the individual slave select pins. Practically, it could have dozens of slaves depending on how the slave select pin is used.

 ⇥ 3. What is the highest SPI communication speed on Arduino?
Arduino SPI can run at up to 8MHz (SPI_CLOCK_DIV2) on a 16MHz Arduino. 2-4MHz speeds are safer for longer lines and multiple devices.

 ⇥  4. Can Arduino SPI Interact with 3.3V Components?  
In brief—yes, but with voltage level conversion. To protect your 5V Arduino, be sure to use level shifters or a resistor voltage divider to 3.3V SPI devices.  

 ⇥ 5. What does SPI offer versus I2C communication?
SPI communication is faster with MHz speeds than I2C information (kHz speeds) and comes out with 4 wires connecting devices in full-duplex mode. The downside was more pins, while higher SPI speeds may become arbitrary.

 ⇥ 6. How do I debug Arduino SPI communication problems?
Check and confirm pin and ground connections, reduce SPI speed, add pull-up resistors on SS lines, and debug data transmissions with serial motors.

 ⇥ 7. Can I utilise any digital pin for Slave Select (SS)?
Yes, any of the digital pins can be used as SS for slave devices. But pin 10 should be kept as output on the master even when using other SS pins.

 ⇥ 8. What if several slaves are chosen at the same time?
It leads to bus contention and garbage data. Always keep one slave selected (SS LOW) at a time using proper slave management in your code.

SPI Communication-Based Projects

Discover how SPI Communication has been applied in our past projects by exploring the references given below.

How to Use SPI Communication in STM32 Microcontroller

How to Use SPI Communication in STM32 Microcontroller

In this tutorial, we will replace one Arduino board with the Blue Pill board, which is the STM32F103C8 and will communicate with the Arduino board using the SPI bus. In this STM32 SPI Example, we will use Arduino UNO as the Slave and STM32F103C8 as the Master with two 16X2 LCDs attached separately. 

SPI Communication with PIC Microcontroller PIC16F877A

SPI Communication with PIC Microcontroller PIC16F877A

If you have been following our PIC tutorials, then you would have noticed we have already covered a wide range of tutorials on the PIC microcontroller, starting from the very basics. Let’s focus on the SPI Protocol for now since that is what we are going to learn in this tutorial.

SPI Communication with Nuvoton N76E003 MCU to Communicate with Arduino

SPI Communication with Nuvoton N76E003 MCU to Communicate with Arduino

We are going to design and study a simple low-voltage Short-circuit protection circuit for DC voltage. The circuit is designed with the purpose of running a microcontroller circuit safely and can protect it from damage due to a short circuit in another part of the circuitry.

Complete Project Code

Master Arduino Code:
//SPI MASTER (ARDUINO)
//SPI COMMUNICATION BETWEEN TWO ARDUINO 
//CIRCUIT DIGEST
#include<SPI.h>                             //Library for SPI 
#define LED 7           
#define ipbutton 2
int buttonvalue;
int x;
void setup (void)
{
  Serial.begin(115200);                   //Starts Serial Communication at Baud Rate 115200 
  
  pinMode(ipbutton,INPUT);                //Sets pin 2 as input 
  pinMode(LED,OUTPUT);                    //Sets pin 7 as Output
  
  SPI.begin();                            //Begins the SPI commnuication
  SPI.setClockDivider(SPI_CLOCK_DIV8);    //Sets clock for SPI communication at 8 (16/8=2Mhz)
  digitalWrite(SS,HIGH);                  // Setting SlaveSelect as HIGH (So master doesnt connnect with slave)
}
void loop(void)
{
  byte Mastersend,Mastereceive;          
  buttonvalue = digitalRead(ipbutton);   //Reads the status of the pin 2
  if(buttonvalue == HIGH)                //Logic for Setting x value (To be sent to slave) depending upon input from pin 2
  {
    x = 1;
  }
  else
  {
    x = 0;
  }
  
  digitalWrite(SS, LOW);                  //Starts communication with Slave connected to master
  
  Mastersend = x;                            
  Mastereceive=SPI.transfer(Mastersend); //Send the mastersend value to slave also receives value from slave
  
  if(Mastereceive == 1)                   //Logic for setting the LED output depending upon value received from slave
  {
    digitalWrite(LED,HIGH);              //Sets pin 7 HIGH
    Serial.println("Master LED ON");
  }
  else
  {
   digitalWrite(LED,LOW);               //Sets pin 7 LOW
   Serial.println("Master LED OFF");
  }
  delay(1000);
}
 
Slave Arduino Code:
//SPI SLAVE (ARDUINO)
//SPI COMMUNICATION BETWEEN TWO ARDUINO 
//CIRCUIT DIGEST
//Pramoth.T
#include<SPI.h>
#define LEDpin 7
#define buttonpin 2
volatile boolean received;
volatile byte Slavereceived,Slavesend;
int buttonvalue;
int x;
void setup()
{
  Serial.begin(115200);
  
  pinMode(buttonpin,INPUT);               // Setting pin 2 as INPUT
  pinMode(LEDpin,OUTPUT);                 // Setting pin 7 as OUTPUT
  pinMode(MISO,OUTPUT);                   //Sets MISO as OUTPUT (Have to Send data to Master IN 
  SPCR |= _BV(SPE);                       //Turn on SPI in Slave Mode
  received = false;
  SPI.attachInterrupt();                  //Interuupt ON is set for SPI commnucation
  
}
ISR (SPI_STC_vect)                        //Inerrrput routine function 
{
  Slavereceived = SPDR;         // Value received from master if store in variable slavereceived
  received = true;                        //Sets received as True 
}
void loop()
{ if(received)                            //Logic to SET LED ON OR OFF depending upon the value recerived from master
   {
      if (Slavereceived==1) 
      {
        digitalWrite(LEDpin,HIGH);         //Sets pin 7 as HIGH LED ON
        Serial.println("Slave LED ON");
      }else
      {
        digitalWrite(LEDpin,LOW);          //Sets pin 7 as LOW LED OFF
        Serial.println("Slave LED OFF");
      }
      
      buttonvalue = digitalRead(buttonpin);  // Reads the status of the pin 2
      
      if (buttonvalue == HIGH)               //Logic to set the value of x to send to master
      {
        x=1;
        
      }else
      {
        x=0;
      }
      
  Slavesend=x;                             
  SPDR = Slavesend;                           //Sends the x value to master via SPDR 
  delay(1000);
}
}
Video

Have any question related to this Article?

Comments

Hello, Great tutorial,  I am going to mess with this a bit to see if it works for me.  Question.  I noticed the "Master" code uses pin10 as SS (Slave Select).  My understand from this article is so that the slave knows wheter communication is bound for it or not.  however in the code for the slave, I don't see the SS utilized.  This would make me thing that the slave unit in this example would listen and respond to any and all communication on the SPI without regard to the masters SS.  Is this correct?  How would you correct this?  in my use case I have multiple slave devices on my SPI bus.

For anyone confused by the resistors, follow the photo not the wiring diagram. That means use10kΩ resistors between the switch and earth / black rail, not 220Ω. It's not a catastrophic error, just allows a bit of excessive / unnecessary current while the button is pressed. 

Excellent tutorial otherwise. Bookmarked.

A fun sketch. But I'm a bit puzzled as how this is intended to work.

  1. In the slave sketch the boolean "received" is set to true first time the ISR is executed and never set to false again.
  2. The slave if-block if(received) {} extends over the entire loop. Hence, the slave only reads and sends its own push button state if it has received data from the master. 
  3. The master has no ISR. So the reaction to receiving data from the slave is delayed until the master loop again reaches the SPI.transfer statement.

As much as I can see (don't have two Arduinos) the slave is chosen to be subordinate compared to the master. While there need to be a master and a slave in the SPI protocol, it should be possible to make the two equal in terms of actions. But that would require the use of ISR on both, I think.

Add New Comment

Login to Comment Sign in with Google Log in with Facebook Sign in with GitHub