SPI communication between slave Arduino and Master Raspberry Pi

Hi everyone,

I'm working on a project - just for learning - where I'm trying to send joystick position - analog data - from Arduino UNO to Raspberry Pi 3B+ (WiringPi Library) using SPI protocol.
So far, I've managed to get both devices communicating using some example code from the internet (i.e. Raspberry Pi (master) Arduino Uno (slave) SPI communication with WiringPi - The Robotics Back-End)

So far, I'm able to send one byte from the master RPi which initiates the ISR on the arduino UNO. The arduino UNO takes the received byte from RPi adds a number to it and assign it back to SPDR which is sent back to the RPi.

Now I'm trying to transmit more bytes - let's say an integer - instead of one byte but I'm not sure how to do it. your inputs are highly appreciated.

Arduio UNO code

#include <SPI.h>

int data;
byte c;
int x_val;

void setup() {
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  pinMode(A5, INPUT);

  Serial.begin(9600);

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPI.attachInterrupt();
}

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  c = SPDR;//Storing the SPI incoming byte
  SPDR = c+5; //Changing SPDR byte to a variable to be sent back to master

}  // end of interrupt service routine (ISR) for SPI

void loop () { 

  x_val = analogRead(A5);//Reading the analog value and assigning it to a variable
  Serial.println(x_val);//Printing this value to serial just to verify 

 }

Raspberry Pi code

#include <iostream>
#include <wiringPiSPI.h>
#include <unistd.h> //for sleep function


#define SPI_CHANNEL 0
#define SPI_CLOCK_SPEED 1000000

int main(int argc, char **argv)
{
    int entered_value = 132;
    
    int fd = wiringPiSPISetupMode(SPI_CHANNEL, SPI_CLOCK_SPEED, 0);
    if (fd == -1) {
        std::cout << "Failed to init SPI communication.\n";
        return -1;
    }
    std::cout << "SPI communication successfully setup.\n";

   
   while(1)
   {
    unsigned char buf[2] = {entered_value, 0};
    wiringPiSPIDataRW(SPI_CHANNEL, buf, 1);

    std::cout << "Data returned: " << +buf[1] << "\n";
    std::cout << "\n";

    sleep(1);
}
  
    return 0;
}

You can send multiple bytes of data. Break down, say, 32 bit data thingies into bytes, send them, and reassemble on the other end.

Thank you @Idahowalker for your response.
Yes, theoretically I should be sending these bytes one by one and re-assemble them. Any idea how to do that using SPDR on the arduino side?

On Raspberry Pi side, I can change the buffer array to contain 4 bytes

unsigned char buf[4] = {entered_value, 0, 0, 0};

Then, the last input in the function " wiringPiSPIDataRW" can be changed to 4 as well to receive the 4 bytes.

wiringPiSPIDataRW(SPI_CHANNEL, buf, 4);" function 

However, I'm not sure how the code will look like on the Arduino UNO side. I'm not sure how I can send a different byte to the RPi during a 4 bytes single transmission session.

OMG! Your internet search on how to break down a multibyte number into individual bytes has failed!

OK, here is some code on how to break down a thingy into individual thingies.

if (!PassTwo)
        {
          rx_frame.FIR.B.FF = CAN_frame_std;
          rx_frame.MsgID = 1;
          rx_frame.FIR.B.DLC = 8;
          rx_frame.data.u8[0] = *item & 0xFF;
          rx_frame.data.u8[1] = (*item >> 8) & 0xFF;
          rx_frame.data.u8[2] = (*item >> 16) & 0xFF;
          rx_frame.data.u8[3] = (*item >> 24) & 0xFF;
          PassTwo = true;
        } else {
          rx_frame.data.u8[4] = *item & 0xFF;;
          rx_frame.data.u8[5] = (*item >> 8) & 0xFF;
          rx_frame.data.u8[6] = (*item >> 16) & 0xFF;
          rx_frame.data.u8[7] = (*item >> 24) & 0xFF;
          ESP32Can.CANWriteFrame(&rx_frame); // send items over CAN buss
          PassTwo = false;
        }

Hi @Idahowalker
Sorry, breaking the bytes wasn't what I was looking for.
I was wondering how to use SPDR to send a different value to the RPi.

Sorry for wasting your time.

I use the ESP32's API when I do SPI thingies.
I have some code here where I write 32 bits to the SPI bus, I doubt it will be of help but I post it just in case.

void fReadSPIdata16bits( spi_device_handle_t &h, int _address )
{
  uint8_t address = _address;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 3); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 8 * 2 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxData;
  txData[0] = address | 0x80;
  intError = spi_device_transmit( h, &trans_desc);
  low = rxData[0]; high = rxData[1];
  //  if ( intError != 0 )
  //  {
  //    Serial.print( " WHO I am LSM9DS1. Transmitting error = ");
  //    Serial.println ( esp_err_to_name(intError) );
  //  }
} // void fSendSPI( uint8_t count, uint8_t address, uint8_t DataToSend)
////
int fWriteSPIdata8bits( spi_device_handle_t &h, int _address, int _sendData )
{
  uint8_t address =  _address;
  uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 0 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = NULL;
  txData[0] = address  & 0x7F;
  txData[1] = sendData;
  intError = spi_device_transmit( h, &trans_desc);
  //  //  if ( intError != 0 )
  //  //  {
  //  //    Serial.print( " LSM9DS1_REGISTER_CTRL_REG6_XL. Transmitting error = ");
  //  //    Serial.println ( esp_err_to_name(intError) );
  //  //  }
  return intError;
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
//
int fWriteSPIdata8bits2( spi_device_handle_t &h, int _sendData )
{
  rxData[0] = 0x00;
  // uint8_t address =  _address;
  uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 8 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxData;
  txData[0] = sendData;
  // txData[0] = address;
  // txData[1] = sendData;
  intError = spi_device_transmit( h, &trans_desc);
  if ( intError == 0 )
  {
    return rxData[0];
  }
  else
  {
  return intError;  
  }
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
////
int fWriteSPIdata32bits( spi_device_handle_t &h, int _sendData0, int _sendData1, int _sendData2, int _sendData3 )
{
  // uint8_t address =  _address;
  // uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 4); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 0 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = NULL;
  txData[0] = (uint8_t)_sendData0; // command bits
  txData[1] = (uint8_t)_sendData1; // lower bits
  txData[2] = (uint8_t)_sendData2; // higher bits
  txData[3] = (uint8_t)_sendData3; // address
  intError = spi_device_transmit( h, &trans_desc);
  return intError;
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
////
int fReadSPIdataXbits( spi_device_handle_t &h, int _readaddress, int *rxbuf, int rxlen )
{
  uint8_t address = _readaddress;
  int8_t rxBuf[rxlen] = {0};
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = ( (8 * 1) + (8 * rxlen)); // total data bits
  trans_desc.tx_buffer = txData ;
  trans_desc.rxlength = 8 * rxlen ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxBuf;
  txData[0] = address;
  // txData[1] = 0x00;
  intError = spi_device_transmit( h, &trans_desc);
  for (int i = 0; i < rxlen; i++)
  {
    rxbuf[i] = rxBuf[i];
  }
 return intError;
} // int fReadSPIdataXbits( spi_device_handle_t &h, int _readaddress, int *rxbuf, int rxlen )

Thanks @Idahowalker

I've managed to send 4 bytes from arduino slave to RPi master. I've changed the code in the RPi by adding a for loop.

RPi Code

#include <iostream>
#include <wiringPiSPI.h>
#include <unistd.h> //for sleep function


#define SPI_CHANNEL 0
#define SPI_CLOCK_SPEED 1000000
unsigned char received_val[4];
int constructed_buffer;

int main(int argc, char **argv)
{
    int entered_value = 132; //this is just for testing it could be any value
    
    int fd = wiringPiSPISetupMode(SPI_CHANNEL, SPI_CLOCK_SPEED, 0);
    if (fd == -1) {
        std::cout << "Failed to init SPI communication.\n";
        return -1;
    }
    std::cout << "SPI communication successfully setup.\n";

   
   while(1) //while loop so I dont need to run the app everytime /just for testing
   {
    unsigned char buf[1] = {entered_value}; //this buffer will be sent over SPI
    
    for(int i=0; i<=3; i++) //This foor loop is used to send 4 bytes
    {
    wiringPiSPIDataRW(SPI_CHANNEL, buf, 1);
    received_val[i]=buf[0]; //the received byte is stored in the char array
    std::cout << "Buf returned "<< i << ": " << int(received_val[i]) << " - "; //will print each received byte just for testing
    }

	//Re-constructing the 4 bytes into one  integer 
    constructed_buffer = received_val[0]  << 24 | received_val[1]<< 16| received_val[2] << 8 | received_val[3];
    std::cout << "constructed_buffer = " << constructed_buffer << "\n";
    std::cout << "\n";

    sleep(1); //delay just to keep things under control
}
  
    return 0;
}

Arduino Code

#include <SPI.h>

byte c;
int x_val;
int counter = 0;
byte buf[6]={9,9,9,9,9,9}; //Just for testing
byte sent_buf[4]={255,0,255,0}; //the four bytes that will be sent to the RPi

void setup() {
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  pinMode(A5, INPUT);

  Serial.begin(9600);

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPI.attachInterrupt();

}

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  c = SPDR;//Storing the SPI incoming byte
  if (c == 132 && counter == 0) //132 is the number we set in the RPi just so we know this is the first byte
  {
  buf[0] = SPDR; 
  SPDR = sent_buf[1]; //this will be second byte on the RPi
  counter =1;
  }

  else if (counter == 1)
  {
  buf[1] = SPDR; 
  SPDR = sent_buf[2]; //this will be third byte on the RPi
  counter =2;
  }

  else if (counter == 2)
  {
  buf[2] = SPDR; 
  SPDR = sent_buf[3]; //this will be fourth byte on the RPi
  counter =3;
  }

  else if (counter == 3)
  {
  buf[3] = SPDR; 
  SPDR = sent_buf[0]; //this will be First byte on the RPi
  counter =0;
  }
  
}  // end of interrupt service routine (ISR) for SPI

void loop () { 

  x_val = analogRead(A5);//Reading the analog value and assigning it to a variable

  
  delay(300);
  }