SPI Slave to Master

I've got two Arduinos, one a master and another a slave. I'm trying to send a number, inputted from the serial monitor slave side and displayed on the masters serial monitor. Obviously a slave cannot initiate a transmission however the slave can send a signal to master to indicate it has data to send. I've wrote the code for the Master and slave side following some examples I've found however I'm getting garbage displayed on the serial monitor.

Master

#include <SPI.h>
#define Slave 9
int i = 0;
byte myData[] = {0x12, 0x34};
int SlaveData;

union
{
  int x;
  byte rxData[2];
} data;

void setup()
{
  Serial.begin(115200);
  SPI.begin ();
  pinMode (Slave, INPUT);
  SPI.setClockDivider(SPI_CLOCK_DIV128); //125 kHz ; difficult at 4 MHz speed
  digitalWrite(SS, LOW);
  delay(100);
}

void loop()
{
  SlaveData = digitalRead(Slave);
  if (SlaveData == HIGH);
  {
  SPI.transfer(myData, sizeof(myData));
  data.rxData[0] = myData[1];  //SPI is simultaneous send and receive 1-byte offset
  data.rxData[1] = myData[0];

  Serial.print("Received data from Slave: ");
  Serial.println(data.x, DEC);
  Serial.println("=========================================================");
  } 
}

Slave

#include <SPI.h>  //needed just for the symbols: MISO, SS
unsigned short Number; //7F80
volatile bool flag1 = false;
int Slave=9;
int i = 0;
void setup()
{
  Serial.begin(115200);
  pinMode(Slave, OUTPUT);
  digitalWrite(Slave, LOW);
  pinMode(MISO, OUTPUT);
  pinMode(SS, INPUT_PULLUP);
  SPCR |= _BV(SPE);
  bitClear(SPCR, 4);  //SPI slave
  SPCR |= _BV(SPIE);
}

void loop()
{
  Serial.println("Number?");
  while(!Serial.available() >0){}
  Number = Serial.parseInt();
  if (Number > 0){
     byte myData[] =
{
  lowByte(Number), highByte(Number)
};
  digitalWrite(Slave, HIGH);
  if (flag1 == false)
  {
    ;
  }
  else
  {
    SPDR = myData[i];//highByte(distancePrimaryCM);
    flag1 = false;
    i++;
    if ( i == 2)
    {
      i = 0;
    }
  }
  digitalWrite(Slave,LOW);
  }
}

ISR(SPI_STC_vect)
{
  flag1 = true;
}

Here is what I get from the serial monitor on the master side, constantly.

Received data from Slave: 13330
=========================================================
Received data from Slave: 4660
=========================================================
Received data from Slave: 4626
=========================================================
Received data from Slave: 13330
=========================================================
Received data from Slave: 4660
=========================================================
Received data from Slave: 4626
=========================================================
Received data from Slave: 13330
=========================================================

This is one of those cases where having the ISR "just set a flag" is not very helpful. You need to service that SPI interupt as quickly as possible.

Use a circular buffer (untested)...

#include <SPI.h>  //needed just for the symbols: MISO, SS
int SlavePin=9;

#define BUFF_SIZE 16
volatile uint8_t buff[BUFF_SIZE];
volatile uint8_t head = 0;
volatile uint8_t tail = 0;
volatile uint8_t count = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(SlavePin, OUTPUT);
  digitalWrite(SlavePin, LOW);
  pinMode(MISO, OUTPUT);
  pinMode(SS, INPUT_PULLUP);
  SPCR |= _BV(SPE);
  bitClear(SPCR, 4);  //SPI slave
  SPCR |= _BV(SPIE);
}

void loop()
{
  Serial.println("Number?");
  while(Serial.available() == 0);
  uint16_t Number = Serial.parseInt();
  if (Number > 0){
    noInterrupts();
    WriteBuffer(lowByte(Number));
    WriteBuffer(highByte(Number));
    interrupts();
  }
}

void WriteBuffer(byte b)
{
  if (count < BUFF_SIZE)
  {
    buff[head] = b;
    head = (head + 1) % BUFF_SIZE;
    count++;
    digitalWrite(SlavePin, count != 0);
  }
}

byte ReadBuffer()
{
  byte b = 0;
  if (count != 0)
  {
    b = buff[tail];
    tail = (tail + 1) % BUFF_SIZE;
    count--;
    digitalWrite(SlavePin, count != 0);
  }
  return b;
}

ISR(SPI_STC_vect)
{
  SPDR = ReadBuffer();
}

You need to detect the change in state on that pin when it goes high, not whether its currently high.

like in the StateChangeDetection example

Alternatively you could have the master fire an interrupt on that pin using RISING,
assuming its an interrupt pin.

Thanks for the reply pcbbc,
It seems I just get constant zeros now.

Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================
Received data from Slave: 0
=========================================================

The tutorial does the following job:

You enter a character (s) from the InputBox of the Serial Monitor of Slave/NANO. The NANO reads the character, places it into the SPDR Register of SPI Port, and then interrupts the Master over DPin-9.

The Master goes to the ISR routine, reads the character from the Slave, saves in a global variable x, sets a flag and then goes to the loop() function.

In the loop() function, the Master checks the flag for true condition and then prints the value of x, clears the flag and keeps roaming within loop() function until next IRQ-signal comes from Slave.

1. Build the following setup (Fig-1).
SPIUnoNAno.png
Figure-1:

2. Upload the following sketch into UNO-SPIMaster:

#include<SPI.h>

volatile bool flag = false;
char x;

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
  //pinMode(SS, OUTPUT);   //at defualt SS is output and HIGH
  digitalWrite(SS, LOW);  //Slave is selected
  //--------------------
  attachInterrupt(digitalPinToInterrupt(2), ISRINTZERO, LOW);
}

void loop()
{
 if(flag == true)
 {
  Serial.print(x);
  flag = false;
 }
}

void ISRINTZERO()
{
  x = SPI.transfer(0x00);
  flag = true; 
}

3. Upload the following sketch into NANO-SPISlave:

#include<SPI.h>

void setup()
{
  Serial.begin(9600);
 // SPI.setClockDivider(SPI_CLOCK_DIV16);//
  pinMode(SS, INPUT_PULLUP);  // ensure SS stays high for now
  pinMode(MISO, OUTPUT);
  SPCR |= _BV(SPE);
  SPCR |= !(_BV(MSTR)); //Arduino is Slave
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  SPI.attachInterrupt();   //interrupt logic is enabled
}

void loop()
{
  byte n = Serial.available();
  if(n !=0)
  {
    char x = Serial.read();
    SPDR = x;
    digitalWrite(9, LOW);  //sending IRQ signal
    digitalWrite(9, HIGH);
  }
}

ISR(SPI_STC_vect)
{
  ;
}

4. Press RESET button on both Arduinos.

5. Open Serial Monitor for Slave at 9600 Bd and with "No line ending" option.

6. Open Serial Monitor for Master at 9600 Bd with "No line ending" option.

7. Enter A12345 in the InputBox of the Serial Monitor of Slave and then click on the Send Button.

8. Check that A12345 has appeared on the Serial Monitor (Fig-2) of Master.
SMQ.png
Figure-2:

You can expand the above idea to exchange data items of any complexity level between the two Arduinos using SPI Port.

SMQ.png

SPIUnoNAno.png

Thanks @GolamMostafa for the reply
For some reason the slave code hasn't been attached?

The Slave code are there now!

@GolamMostafa, you are a god amongst men. Thanks a lot. That really has helped. Just one quick question about the speed of things. Can this work at 2MHz instead of 1MHz? Will it be stable?

Callum_Austin11:
@GolamMostafa, you are a god amongst men. Thanks a lot. That really has helped. Just one quick question about the speed of things. Can this work at 2MHz instead of 1MHz? Will it be stable?

Have you tried?

Just replace the following line in Master sketch

SPI.setClockDivider(SPI_CLOCK_DIV16);// 16 MHz/16 =  MBits/s

By the following line:

SPI.setClockDivider(SPI_CLOCK_DIV8);//16 MHz/8 = 2 MBits/s

I have my own experimental result?

Callum_Austin11:
@you are a god amongst men. Thanks a lot. That really has helped. Just one quick question about the speed of things. Can this work at 2MHz instead of 1MHz? Will it be stable?

The Scripture of Islam bears this message: when Allah (God in Christianity; I have no idea about god) wishes to create something, HE just says -- "Be it and it is there."

On the other hand, a man makes a lot of trials-and-errors before he finds a stable result.

Yes sorry, I did indeed change the speed and it worked fine. I only asked as i thought you might of had more experience with it. Thanks a lot though.

@GolamMostafa, thanks for the help with the code. I've come across a problem with it that I'm not too sure on how to fix. I'm trying to send 8000 to the master from the slave and then taking 8000 on the master side and minus 1000. It doesn't seem to work and gives me a weird output on the serial monitor.
-944-952-952-952

Any idea on how to fix this?
Seems as though the number is being sent in separate bits.

Is 8000 in decimal? Is 1000 in decimal?

Do you see that the Serial Monitor should show: 8000 (decimal) - 1000 (decimal) = 7000 (decimal)?

Yes these are just decimal numbers.

Master Code

#include<SPI.h>

volatile bool flag = false;
char x;
int newvalue;


void setup()
{
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
  //pinMode(SS, OUTPUT);   //at defualt SS is output and HIGH
  digitalWrite(SS, LOW);  //Slave is selected
  //--------------------
  attachInterrupt(digitalPinToInterrupt(2), ISRINTZERO, LOW);
}

void loop()
{
 if(flag == true)
 {
  newvalue = x - 1000;
  Serial.print(newvalue);
  
 
  flag = false;
 }
}

void ISRINTZERO()
{
 x = SPI.transfer(0x00);
  flag = true;
}

Slave Code

#include<SPI.h>

void setup()
{
  Serial.begin(9600);
 // SPI.setClockDivider(SPI_CLOCK_DIV16);//
  pinMode(SS, INPUT_PULLUP);  // ensure SS stays high for now
  pinMode(MISO, OUTPUT);
  SPCR |= _BV(SPE);
  SPCR |= !(_BV(MSTR)); //Arduino is Slave
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  SPI.attachInterrupt();   //interrupt logic is enabled
}

void loop()
{
  byte n = Serial.available();
  if(n !=0)
  {
    char x = Serial.read();
    SPDR = x;
    digitalWrite(9, LOW);  //sending IRQ signal
    digitalWrite(9, HIGH);
  }
}

ISR(SPI_STC_vect)
{
  ;
}

Serial monitor Master side (inputting 8000 Slave side)

-944-952-952-952

1. I am getting 7000 in the Serial Monitor of SPI-Master (Fig-1).
sm1000.png
Figure-1:

2. There is no change in the Slave sketch.

3. This is the revised SPI-Master sketch.

#include<SPI.h>

volatile bool flag = false;
char x;
char myData[5] = "";
int i = 0;

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
  //pinMode(SS, OUTPUT);   //at defualt SS is output and HIGH
  digitalWrite(SS, LOW);  //Slave is selected
  //--------------------
  attachInterrupt(digitalPinToInterrupt(2), ISRINTZERO, LOW);
}

void loop()
{
  if (i != 4)
  {
    if (flag == true)
    {
      // Serial.print(x);
      myData[i] = x;
      i++;
      flag = false;
      //Serial.print(i);
    }
  }
  else
  {
    unsigned int myDec = atoi(myData);
    myDec = myDec - (unsigned int)1000;
    Serial.print(myDec);      //8000
    i = 0;
    memset(myData, 0, 5);   //array reset
    flag = false;
  }
}

void ISRINTZERO()
{
  x = SPI.transfer(0x00);
  flag = true;
}

4. You may find this post reading useful on UART communication.

sm1000.png

It seems as though it only works for 8000 :smiley:
Put any other value and you get this;
6453664536

Its almost like you want to collect the incoming serial data into an array (like you have done) and then store that array as just a value. I'm really not sure on how to do it. Been trying things all day.

Look!

1. AE says that the wise people starts with simple thing and then gradually moves to the complex one.

2. Then there is the methodology called SSS Strategy.
(1) You start with a basic thing (simple and elementary). Test it and then add the next incremental hardware and software and test it.

(2) This way, you can always arrive at the generalized solution where the system works for all kinds of defined data set.

3. It works for 8000; because, I have designed it to work for 8000 and 4-digit and you said so. Now, it is your sincere efforts that are to be dedicated to modify the program so that it works for other data item of your interest. What can I do? I can only prescribe few guide lines!

Thanks for the interest.