SPI master requests data from slave, slave sends data to master

I would like to connect two Arduino Nano together. Both of them have sensors connected using I2C. Now I need to send on integer value from the slave to the master, but only when the master requests this. This should be done using SPI, for that each Nano has his own I2C bus.

Searching the web I found a lot of useful information here Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino, but since I am a newbie to Arduino I was not able to change the code from "How to get a response from a slave" so that I can send an integer value from the slave to the master. The integer value is something between -4096 to 4096.

Does anybody have a sample code for slave and master for this task?

I was not able to change the code from “How to get a response from a slave” so that I can send an integer value from the slave to the master.

What code? What changes did you make? What does the slave actually send? What does the master actually get?

Did you get these to work to start from Nick’s page?

Master: This sends “Hello, world” every second to the slave.

// Written by Nick Gammon
// February 2011


#include <SPI.h>

void setup (void)
{

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
}  // end of setup


void loop (void)
{

  char c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Hello, world!\n" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (1000);  // 1 seconds delay 
}  // end of loop

Slave: The slave is entirely interrupt-driven, thus it can doing other stuff. The incoming SPI data is collected in a buffer, and a flag set when a “significant byte” (in this case a newline) arrives. This tells the slave to get on and start processing the data.

// Written by Nick Gammon
// February 2011


#include <SPI.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
  Serial.begin (115200);   // debugging

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
  
  // get ready for an interrupt 
  pos = 0;   // buffer empty
  process_it = false;

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

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
  
  // add to buffer if room
  if (pos < sizeof buf)
    {
    buf [pos++] = c;
    
    // example: newline means time to process buffer
    if (c == '\n')
      process_it = true;
      
    }  // end of room available
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;  
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // end of flag set
    
}  // end of loop

Yes, I was able to run the "Hello World" example from Nick's page. I was also able to run the code where the master sends data to the slave, and then the slave sends data back to the master. But I was not able to change to code in such a way that the master can request the data from an analogRead() from the slave.

But I was not able to change to code in such a way that the master can request the data from an analogRead() from the slave.

Why not? All you need is a text editor.

What does your code look like? What does it do that you don't want? What does it not do that you want?

That’s the code for the Slave:

#include <SPI.h>
#include "pins_arduino.h"


const int sensorPin = 0;
volatile int sensorValue;
volatile int accA;
volatile int accB;

// what to do with incoming data
byte command = 0;

// start of transaction, no command yet
void ss_falling ()
{
  command = 0;
}  // end of interrupt service routine (ISR) ss_falling

void setup (void)
{

    Serial.begin (38400); 
  
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);

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

  // turn on interrupts
  SPCR |= _BV(SPIE);

  // interrupt for SS falling edge
  attachInterrupt (0, ss_falling, FALLING);
  
}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
  byte c = SPDR;
 
  switch (command)
  {
  // no command? then this is the command
  case 0:
    command = c;
    SPDR = 0;
    break;
    
  // add to incoming byte, return result
  case 'a':
    SPDR = accA;  // Send first byte
    break;
    
  // subtract from incoming byte, return result
  case 's':
    SPDR = accA;  // Send second byte
    break;

  } // end of switch

}  // end of interrupt service routine (ISR) SPI_STC_vect


void loop (void)
{
  noInterrupts();
  sensorValue = analogRead(sensorPin);
  sensorValue = map(sensorValue,0,1023,-4000,4000);
  sensorValue = int(sensorValue);
  accA = int((4000 + sensorValue)/ 255);   //Split value in two integers by division and substraction of result
  accB = int(sensorValue + 4000) - (accA * 255); //Split value in two integers by division and substraction of result
  int accR = (accA * 255 + accB)-4000;   //Reconstruction of integer value using two bytes
  interrupts();

  Serial.print(" Potentiometer ");
  Serial.print(sensorValue);
  Serial.print(" accA ");
  Serial.print(accA);
  Serial.print(" accB ");
  Serial.print(accB);
  Serial.print(" accR ");
  Serial.print(accR);

  Serial.println();


  // if SPI not active, clear current command
  if (digitalRead (SS) == HIGH)
    command = 0;
}  // end of loop

And this is the code for the Master:

// Written by Nick Gammon
// April 2011

#include <SPI.h>
#include "pins_arduino.h"

int accA;
int accB;
byte a,b;

void setup (void)
{
  Serial.begin (38400);
  Serial.println ();

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);

}  // end of setup


byte transferAndWait (const byte what)
{
  byte a = SPI.transfer (what);
  delayMicroseconds (20);
  return a;
} // end of transferAndWait


void loop (void)
{

  byte a, b;
  
  // enable Slave Select
  digitalWrite(SS, LOW);    

  transferAndWait ('a');  // add command
  transferAndWait (10);
  a = transferAndWait (0);

  // disable Slave Select
  digitalWrite(SS, HIGH);
  
  // enable Slave Select
  digitalWrite(SS, LOW);   

  transferAndWait ('s');  // subtract command
  transferAndWait (10);
  b = transferAndWait (0);

  // disable Slave Select
  digitalWrite(SS, HIGH);

  int sensorValue = ((a*255 + b)-4000);

  Serial.print(" a ");
  Serial.print (a,DEC);
  Serial.print(" b ");
  Serial.print (b,DEC);
  Serial.print(" accB ");
  Serial.print (b,DEC);
  Serial.print (" Sensor Value ");
  Serial.println (sensorValue);

  delay (50);  // 1 second delay 
}  // end of loop

What am I doing wrong?

  noInterrupts();
  sensorValue = analogRead(sensorPin);
  sensorValue = map(sensorValue,0,1023,-4000,4000);
  sensorValue = int(sensorValue);
  accA = int((4000 + sensorValue)/ 255);   //Split value in two integers by division and substraction of result
  accB = int(sensorValue + 4000) - (accA * 255); //Split value in two integers by division and substraction of result
  int accR = (accA * 255 + accB)-4000;   //Reconstruction of integer value using two bytes
  interrupts();

Why are you turning interrupts off?

Why not do the mapping on the other end? Then, you wouldn't have to f**king around with accA, accB, or accR.

command is used in loop() and in the ISR, but is not declared volatile.

The interrupts are turned off because later on sensors will send data over I2C. The idea is not to interrupt the I2C communication with the sensors. And mapping will not be necessary, I just want to simulate the output that the slave Nano will create after fusion of all the sensor values. And this output must be sent on request to the master Nano.