Nick Gammon's SPI write/read anything slave receiving wrong data

I am trying to use a mega to control 144 nanos via SPI control. I was hoping to connect them all on the same SS (since I don't need data back from each slave) and send an array of positions, using unique ID for each nano to find out the required position from the array.
The example Nick Gammon showed works, but when I try to extend it to a single int array, the slave picks up a lot of gibberish instead of the actual data, which I suspect is from syncing issue as it switches back and forth between gibberish and correct data.

Here's master:

// master (keep track of total size)

// classes needed
#include <SPI.h>
#include "SPI_anything.h"

// module identification
const int rowSize = 12;
const int heightSize = 12;
const int totalSize = rowSize*heightSize;

// create a structure to store the different data values:
typedef struct myStruct
{
	int pos[totalSize];
};

myStruct dataSet;

// convert letter ID into position array pointer 
int convertID(int rowID, int heightID, int rowSize){
	int arrayPointer = rowSize * heightID + rowID;
	return arrayPointer;
}

// Assign position of each module (later needs to be integrated with Unity)
// Currently randomly generated
void assignPosition(){
	for(int module=0; module<totalSize; module++){
	    dataSet.pos[module] = random(1, 100);
	}
}

void setup ()
{
	Serial.begin(9600);

	pinMode(LED_BUILTIN, OUTPUT);
	pinMode(MOSI, OUTPUT);
	pinMode(SS, OUTPUT);
	pinMode(MISO, INPUT);
	pinMode(SCK, OUTPUT);

	SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));

	assignPosition();
}  // end of setup

void loop () 
{
	digitalWrite(SS, LOW);    // Turn on Modules
	SPI_writeAnything (dataSet);
	digitalWrite(SS, HIGH);
	
	Serial.println(dataSet.pos[0]);
	delay (1000);  // for testing  

	assignPosition();	
}  // end of loop

Here's slave

// Slave ID: Row 0, Height 0

// Classes needed
#include <SPI.h>
#include "SPI_anything.h"

// Module identification
const int rowSize = 12;
const int heightSize = 12;
const int totalSize = rowSize*heightSize;

const int slaveRowID = 0;
const int slaveHeightID = 0;
const int slavePointer = rowSize * slaveHeightID + slaveRowID;

int switchLowPin = 2;
int switchHighPin = 3;
int switchMiddlePin = 4;
int encoderPin = 5;
int servoPin = 6; 

int slavePosition;

// Create a structure to store the different data values:
typedef struct myStruct
{
	int pos[totalSize];
};

myStruct dataSet;

// Setup
void setup ()
{
	Serial.begin (9600);   // debugging
	Serial.print("Output of ");
	Serial.println(slavePointer);

	pinMode(MISO, OUTPUT);

	pinMode(switchLowPin, INPUT);
	pinMode(switchMiddlePin, INPUT);
	pinMode(switchHighPin, INPUT);
	pinMode(encoderPin, INPUT);
	pinMode(servoPin, OUTPUT);

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

void loop () 
{
	SPI_readAnything (dataSet);
	Serial.println(dataSet.pos[slavePointer]);
}

Here's SPI_anything.h:

#include <Arduino.h>

template <typename T> unsigned int SPI_writeAnything (const T& value)
  {
    const byte * p = (const byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          SPI.transfer(*p++);
    return i;
  }  // end of SPI_writeAnything

template <typename T> unsigned int SPI_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = SPI.transfer (0);
    return i;
  }  // end of SPI_readAnything
  
  
template <typename T> unsigned int SPI_readAnything_ISR(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    *p++ = SPDR;  // get first byte
    for (i = 1; i < sizeof value; i++)
          *p++ = SPI.transfer (0);
    return i;
  }  // end of SPI_readAnything_ISR

Am I understanding the source of the problem properly? Or is it another issue?
Many thanks in advance!

Screenshot 2021-01-15 152032.png

Screenshot 2021-01-15 152032.png

When I changed the CLK speed to 1Mhz instead, I am getting a more consistent result, but still with long periods of wrong data. Why would the clocks be out of sync if SS is connected though?

Ok, so here's a quick update; I changed a bit of the code to see if the data was potentially shifting, and turns out it is, but semi randomly. I can't figure out what the trigger is though.

New Master (changed, int array to byte array, added counter)

// master (keep track of total size)

// classes needed
#include <SPI.h>
#include "SPI_anything.h"

// module identification
const int rowSize = 12;
const int heightSize = 12;
const int totalSize = rowSize*heightSize;

int counter = 0;

// create a structure to store the different data values:
typedef struct myStruct
{
  byte pos[totalSize];
};

myStruct dataSet;

// convert letter ID into position array pointer 
int convertID(int rowID, int heightID, int rowSize){
  int arrayPointer = rowSize * heightID + rowID;
  return arrayPointer;
}

// Assign position of each module (later needs to be integrated with Unity)
// Currently randomly generated
void assignPosition(){
  for(int module=0; module<totalSize; module++){
      dataSet.pos[module] = module+1;
  }
}

void setup ()
{
  Serial.begin(9600);

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, OUTPUT);
  pinMode(MISO, INPUT);
  pinMode(SCK, OUTPUT);

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));

  assignPosition();
}  // end of setup

void loop () 
{
  digitalWrite(SS, LOW);    // Turn on Modules
  SPI_writeAnything (dataSet);
  digitalWrite(SS, HIGH);
  
  Serial.println(counter++);
  delay (2000);  // for testing  

  assignPosition(); 
}  // end of loop

New Slave (list out whole array)

// Slave ID: Row 0, Height 0

// Classes needed
#include <SPI.h>
#include "SPI_anything.h"

// Module identification
const int rowSize = 12;
const int heightSize = 12;
const int totalSize = rowSize*heightSize;

const int slaveRowID = 0;
const int slaveHeightID = 0;
const int slavePointer = rowSize * slaveHeightID + slaveRowID;

int switchLowPin = 2;
int switchHighPin = 3;
int switchMiddlePin = 4;
int encoderPin = 5;
int servoPin = 6; 

int slavePosition;
int counter = 0;

// Create a structure to store the different data values:
typedef struct myStruct
{
  byte pos[totalSize];
};

myStruct dataSet;

// Clear data
void clearPosition(){
  for(int module=0; module<totalSize; module++){
      dataSet.pos[module] = 0;
  }
}

// Setup
void setup ()
{
  Serial.begin (9600);   // debugging
  Serial.print("Output of Slave ");
  Serial.println(slavePointer);
  
  clearPosition();

  pinMode(MISO, OUTPUT);

  pinMode(switchLowPin, INPUT);
  pinMode(switchMiddlePin, INPUT);
  pinMode(switchHighPin, INPUT);
  pinMode(encoderPin, INPUT);
  pinMode(servoPin, OUTPUT);

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

void loop () 
{
  SPI_readAnything (dataSet); 

  for(int module=0; module<totalSize; module++){
    Serial.println(dataSet.pos[module]);
  }

  clearPosition();

}

I think it's quite 50/50 between ending at 143 and ending at 144, with occasional data jumble in between switching.

With 144 nano's, I am guessing that there is some length of cable between the master and final slave device. You may find that the clock speed is too high for the length of cable you are using. What happens if you reduce the clock speed down?

markd833:
With 144 nano's, I am guessing that there is some length of cable between the master and final slave device. You may find that the clock speed is too high for the length of cable you are using. What happens if you reduce the clock speed down?

Well for now I am simply testing with a single slave. The final design should place them less than a meter away from each other. I tried slowing the clock down, and it was producing a more consistent result for both 143 and 144 (flipping less often), so not quite helpful. Currently I'm exploring just ditching write/read_anything and simply using SPI.transfer with simpler data set formats.

Slow the clock down some more.

The SPI bus was designed for short distance communications between chips and modules. For long distances use RS-232, RS-485 or CAN-bus.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.