Error in repeated(looped) SPI data transmission

Hi,
Apologies if my question is in the wrong category as I'm new here, and couldnt find similar questions after searching for 15 minutes or so.
I'm trying to communicate between 2 arduino mega2560 boards using SPI, master board need to transmit 3~10 variables ( uint8 for now, but will do float later) every 500ms to the slave board, I borrowed a piece of example code online and it works very well if I only transmit these variables once, however if I do it repeatedly every 500ms, occasionally the slave terminal will display the "incorrect" numbers, which is different from what I put in the master, not sure if it's error in the SPI transmission or something else, hope to get your help on that and thank you very much
master code:

// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino

#include <SPI.h>


unsigned long tt=0;


int cnt;

uint8_t roll_x,pitch_x,yaw_x;

void setup (void)
{
  Serial.begin (115200);   // debugging
  
  digitalWrite(SS, HIGH);  // ensure SS stays high for now

 
  SPI.begin ();

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


void loop (void)
{
  tt=millis();
  cnt=cnt+1;
  char c;

   pitch_x=1+cnt;
   roll_x=2+cnt;
   yaw_x=3+cnt;
  // enable Slave Select
  digitalWrite(SS, LOW);    
  
   SPI.transfer(pitch_x);
   delayMicroseconds(100);
   SPI.transfer(roll_x);
   delayMicroseconds(100);
   SPI.transfer(yaw_x);
   delayMicroseconds(100);

   c='\n';
   SPI.transfer(c);
  // disable Slave Select
  digitalWrite(SS, HIGH);
  
  delay (500-(millis()-tt));  // 05 second delay
}  // end of loop

slave code:

// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino

#include <SPI.h>

unsigned long tt=0;
int tcnt;
uint8_t i;

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

uint8_t pitch_x,roll_x,yaw_x;

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

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

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

  // get ready for an interrupt
  pos = 0;   // buffer empty
  process_it = false;

   tt=0;
   tcnt=0;
}  // end of setup


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


// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
      pitch_x=buf[0];
      roll_x=buf[1];
      yaw_x=buf[2];
      Serial.print("transmitted pitch roll yaw are: ");
      Serial.print(pitch_x);
      Serial.print(" ");
      Serial.print(roll_x);
      Serial.print(" ");
      Serial.println(yaw_x);
      pos = 0;
      process_it = false;
    }  // end of flag set
 
}  // end of loop

slave output snapshots (error after 71 72 73 and after 79 80 81):
transmitted pitch roll yaw are: 65 66 67
transmitted pitch roll yaw are: 66 67 68
transmitted pitch roll yaw are: 67 68 69
transmitted pitch roll yaw are: 68 69 70
transmitted pitch roll yaw are: 69 70 71
transmitted pitch roll yaw are: 70 71 72
transmitted pitch roll yaw are: 71 72 73
transmitted pitch roll yaw are: 73 74 75
transmitted pitch roll yaw are: 74 75 76
transmitted pitch roll yaw are: 75 76 77
transmitted pitch roll yaw are: 76 77 78
transmitted pitch roll yaw are: 77 78 79
transmitted pitch roll yaw are: 78 79 80
transmitted pitch roll yaw are: 79 80 81
transmitted pitch roll yaw are: 5 21 32
transmitted pitch roll yaw are: 84 85 86
transmitted pitch roll yaw are: 85 86 87
transmitted pitch roll yaw are: 86 87 88
transmitted pitch roll yaw are: 87 88 89
transmitted pitch roll yaw are: 88 89 90
transmitted pitch roll yaw are: 89 90 91

To detect the source of errors, send constant values (For example: pitch = 45, roll = 68, yaw = 27) from Master to Slave; check that the Slave receives/display the parameters correctly at 1-sec interval.

Note that due to circulating nature of the SPI's Data Register, there is one-byte offset between transmission and reception (Fig-1).

spi328latest
Figure-1:

Could you turn on the Serial Monitor timestamps to see if the messages are arriving regularly at 500-millisecond intervals?

How does the receiver know that the '\n' is the end of the message when the data could contain a byte with the same value (10 == 0x0A == '\n')?

@GolamMostafa thank you very much for the reply, do you mean on the master, the offset exist between the return value of SPI.transfer() and the byte actually sent?
Also I tried using constant value as you suggested, below are the output, still some error when msg are not received every 500ms

@johnwasser thanks for pointing these out and I did modify the master to send the constant value without 10 to avoid the issue with '\n' , also I turned on the time stamp, below are the output, looks like when the slave did not receive msg every 500ms as it supposed to, the error occurs. Wonder what could have caused that

salve Serial output:
12:51:16.617 -> transmitted pitch roll yaw are: 45 68 27
12:51:17.098 -> transmitted pitch roll yaw are: 45 68 27
12:51:17.616 -> transmitted pitch roll yaw are: 45 68 27
12:51:18.586 -> transmitted pitch roll yaw are: 45 16 108
12:51:20.108 -> transmitted pitch roll yaw are: 45 68 97
12:51:21.101 -> transmitted pitch roll yaw are: 45 68 97
12:51:21.612 -> transmitted pitch roll yaw are: 45 68 27
12:51:23.614 -> transmitted pitch roll yaw are: 45 16 108
12:51:24.094 -> transmitted pitch roll yaw are: 45 68 27
12:51:24.609 -> transmitted pitch roll yaw are: 45 68 27
12:51:25.096 -> transmitted pitch roll yaw are: 45 68 27
12:51:25.609 -> transmitted pitch roll yaw are: 45 68 27

Thank you

I'm curious as to how far apart your Arduinos are and what wire are you using to connect the signals?

@ToddL1962

I was using fairly standard 16in arduino wires with dupont connections ( since the board has these), I just tried with 8in wires, it seems to have solved the problem, wonder if it's just simply faulty wires/flimsy connections as I dont see these two length can make that big of a difference.

Thank you

1. Can we see the output now?

2.
spiDistance

3. The above are fairly constant value; needs investigation.

4. Can you please try to test the following sketches with your original setup (wires/distance) and check the responses? Data transfer rate : 1 MBits/sec (your's were 2 MBits/se). You may change my sketches to run at 2 MBits/sec.

Master sketch collects 4-byte data from Slave.

#include<SPI.h>
byte myData[] = {0x00, 0x00, 0x00, 0x00};
void setup()
{
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
  //pinMode(SS, OUTPUT);
  digitalWrite(SS, LOW);  //Slave is selected
  //--------------------
}

void loop()
{
  for (int i = 0; i < 4; i++)
  {
    myData[i] = SPI.transfer(myData[i]);
    //delayMicroseconds(100); //allows Slave to process byte
    Serial.println(myData[i], HEX);  //shows: 0xAB
  }
  long x = (long)myData[1] << 24 | (long)myData[2] << 16 | (long)myData[3] << 8 | (long)myData[0];
  Serial.println(x, HEX); //shows: 0x123456AB
  Serial.println("======================");
  delay(1000);  //test interval
}

Slave sketch sends 4-byte data to Master in response to query.

#include<SPI.h>
int i = 0;
byte myData[] = {0x12, 0x34, 0x56, 0xAB};

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
  SPI.attachInterrupt();   //interrupt logic is enabled
}

void loop()
{
}

ISR(SPI_STC_vect)
{
  SPDR = myData[i]; //places 0x12, then 0x34, then 0x56, then 0xAB
  i++;
  if (i == 4)     //4-byte data are sent
  {
    i = 0;          //array pointer is reset
  }
}

5.

Yes!

thanks for the note, please see below for 1, 3, 4
1 ( after swapping to shorter wire and with 50ms loop)
10:40:43.616 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.649 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.716 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.750 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.817 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.853 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.924 -> transmitted pitch roll yaw are: 45 68 27
10:40:43.957 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.024 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.057 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.124 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.162 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.229 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.264 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.299 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.367 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.401 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.467 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.501 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.569 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.603 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.670 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.704 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.774 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.808 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.879 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.913 -> transmitted pitch roll yaw are: 45 68 27
10:40:44.947 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.015 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.085 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.122 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.156 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.224 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.257 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.325 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.358 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.396 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.465 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.501 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.568 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.602 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.668 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.701 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.773 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.806 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.874 -> transmitted pitch roll yaw are: 45 68 27
10:40:45.910 -> transmitted pitch roll yaw are: 45 68 27

  1. yea not sure what happened my guess it's related to wires/connections

  2. just tried and it seems to consistently output the expect values regardless of cable length and/or clock rate as follows
    10:53:41.979 -> AB
    10:53:41.979 -> 12
    10:53:41.979 -> 34
    10:53:41.979 -> 56
    10:53:42.012 -> 123456AB
    10:53:42.012 -> ======================
    10:53:42.984 -> AB
    10:53:42.984 -> 12
    10:53:42.984 -> 34
    10:53:42.984 -> 56
    10:53:43.021 -> 123456AB
    10:53:43.021 -> ======================
    10:53:43.985 -> AB
    10:53:43.985 -> 12
    10:53:43.985 -> 34
    10:53:43.985 -> 56
    10:53:43.985 -> 123456AB
    10:53:44.021 -> ======================
    10:53:44.975 -> AB
    10:53:45.012 -> 12
    10:53:45.012 -> 34
    10:53:45.012 -> 56
    10:53:45.012 -> 123456AB
    10:53:45.012 -> ======================
    10:53:45.976 -> AB
    10:53:46.009 -> 12
    10:53:46.009 -> 34
    10:53:46.009 -> 56
    10:53:46.009 -> 123456AB
    10:53:46.009 -> ======================

Thank you

So, what is your conclusion?

I would suggest to revise your sketch of Post-1 in the light of the logic of the sketch of Post-7 and check that the Slave makes consistent response within allowed operating distance/bit rate.

I think your way is better and more stable, since the one I borrowed still doesnt work with the re plugged 16in wires, though I can get around with 8in wires it seems. I need to think about how to use your logic down the road when I need to talk to multiple slaves from single/multiple masters.
Thank you

Here is an example of exchanging 2-byte data between Master and Slave at 1-sec interval. You may follow the logic of these sketches to tailor your original sketches and then check with long wires.

Master Codes:

#include<SPI.h>
byte myData[] = {0x12, 0x34};//,
byte recData[2];

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
  //pinMode(SS, OUTPUT);
  digitalWrite(SS, LOW);  //Slave is selected
  //--------------------
}

void loop()
{
  for (int i = 0; i < 2; i++)
  {
    recData[i] = SPI.transfer(myData[i]); //sends: 
    //delayMicroseconds(100); //allows Slave to process received byte
    Serial.println(recData[i], HEX);  //shows: 0xAB
  }
  int x = (int)recData[1] << 8 | (int)recData[0];
  Serial.println(x, HEX); //shows: 0x5678
  Serial.println("======================");
  delay(1000);  //test interval
}

Slave Codes:

#include<SPI.h>

byte txData[] = {0x56, 0x78};
byte rxData[2];
volatile int i = 0;
volatile bool flag = false;

void setup()
{
  Serial.begin(9600);
  bitClear(SPCR, MSTR);
  bitSet(SPCR, SPE);  //must be
  pinMode(SS, INPUT_PULLUP);
  pinMode(MOSI, INPUT);
  pinMode(MISO, OUTPUT);
  pinMode(SCK, INPUT);
  SPI.attachInterrupt();
}

void loop()
{
  if (flag == true)
  {
    Serial.println(rxData[0], HEX);
    Serial.println(rxData[1], HEX);
    flag = false;
    Serial.println("==================");
  }
}

ISR(SPI_STC_vect)
{
  if (flag == false)//this flag syncheonizes data with Master
  {
    rxData[i] = SPDR;
    SPDR = txData[i];
    i++;
    if (i == 2)
    {
      flag = true;
      i = 0;
    }
  }
}

@xkshen

Your revised Master/Slave Codes are given below; you may test them with long wires:

Master Codes:

// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino

#include <SPI.h>

//unsigned long tt = 0;
//int cnt;
uint8_t roll_x = 0x68, pitch_x = 0x45, yaw_x = 0x27; //known values

void setup (void)
{
  Serial.begin (115200);   // debugging
  digitalWrite(SS, HIGH);  // ensure SS stays high for now
  SPI.begin ();
  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  tt = 0;
  //cnt = 0;
     digitalWrite(SS, LOW);  //Slave is selected
}  // end of setup

void loop (void)
{
  //tt = millis();
 // cnt = cnt + 1;
 // byte c;

  //pitch_x = 1 + cnt;
  //roll_x = 2 + cnt;
  //yaw_x = 3 + cnt;
  // enable Slave Select
  //digitalWrite(SS, LOW);

  SPI.transfer(pitch_x);
  delayMicroseconds(100);
  SPI.transfer(roll_x);
  delayMicroseconds(100);
  SPI.transfer(yaw_x);
  delayMicroseconds(100);

  //c = '\n';
  //SPI.transfer(c);
  // disable Slave Select
 // digitalWrite(SS, HIGH);

  delay (500);// - (millis() - tt)); // 05 second delay
}  // end of loop

Slave Codes:

// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino

#include <SPI.h>

//unsigned long tt = 0;
//int tcnt;
uint8_t i;

volatile byte buf [3];
volatile byte pos = 0;
volatile bool process_it = false;

uint8_t pitch_x, roll_x, yaw_x;

void setup (void)
{
  Serial.begin (115200);   // debugging
  pinMode(SS, INPUT_PULLUP); //ensures Slve is OFF
  // turn on SPI in slave mode
  SPCR |= bit (SPE);
  SPCR |= !(_BV(MSTR)); //Arduino is Slave
  SPI.attachInterrupt(); //
  // SPCR |= bit (SPIE);

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

  // get ready for an interrupt
  // pos = 0;   // buffer empty
  //process_it = false;

  //  tt = 0;
  //tcnt = 0;
}  // end of setup


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


// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
  {
    pitch_x = buf[0];
    roll_x = buf[1];
    yaw_x = buf[2];
    Serial.print("Received pitch roll yaw are: ");
    Serial.print(pitch_x);
    Serial.print(" ");
    Serial.print(roll_x);
    Serial.print(" ");
    Serial.println(yaw_x);
    pos = 0;
    process_it = false;
  }  // end of flag set

}  // end of loop

thanks for the update, though it doesn't seem to work with the longer wires in my case.
I suspect the "interrupt" is causing trouble while the slave code is in the "process_it" stage, and am looking into some sort of "handshake" scheme such that when the slave is doing "process_it", the master will wait til the slave is done "process_it", then send out the bytes. Hope that will solve the problem.

Thanks