SPI - Slave Send A String To Master Example

I am going to contribute a little bit here. I hope it helps a little.

I am still new to Arduino and SPI. There are still many things that I don't understand well.

I was following Nick's tutorial closely here on how to send data back to the master from slave.

However, that tutorial shows you how to send a byte back to the master. If let say you want to send a string of text back to the master, its not as easy as I thought.

Ok, here is a code that is not working. Please note that it is not the complete code. I only show you the necessary codes that involves SPI communcations.

//master

int gpsSlavePin = 49;
const int GPS_LENGTH = 29; 

void setup () {
  pinMode(gpsSlavePin, OUTPUT);
  digitalWrite(gpsSlavePin, 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);
   
  Serial.begin(115200);
  Serial.println("Master initialized");
  
  getGpsCoordinates();
}  // end of setup

void loop () {
}  // end of loop

//This, its not gonna work. You are gonna get garbage responses from the slave
void getGpsCoordinates() {
  // enable Slave Select
  String testString = "";
  digitalWrite(gpsSlavePin, LOW);    // SS is pin 49
  for(int i=0; i<GPS_LENGTH; i++) {  //GPS_LENGTH
    char response = SPI.transfer(GPS_COORDINATES);
    //testString += response;
    Serial.print(response);
  }
  digitalWrite(gpsSlavePin, HIGH); // disable Slave Select
}
//slave
#define  GPS_COORDINATES   0x0B
char coordinatesArray[] = "Lat: 1.123456 Lon: 103.123456";

void setup() {
   Serial.begin(115200);
   pinMode(MISO, OUTPUT);  //have to send on MISO, pin 12 on UNO
   SPCR |= _BV(SPE);  //Turn on SPI in slave mode
   SPI.attachInterrupt();  //turn on interrupt
 }
 
 // SPI interrupt routine
 ISR (SPI_STC_vect) {
   byte c = SPDR;  //grab a byte from SPI data register
   switch(c) {
     case GPS_COORDINATES:
       SPDR = coordinatesArray[counter]; //THIS WORKS
       bGpsCoordinates = true;
       break;
   }
 }
 
 // wait for flag to set in interrupt routine
 void loop() {
    if(bGpsCoordinates) {
      charToSend = coordinatesArray[counter];
      counter++;
      Serial.print(charToSend);
      bGpsCoordinates = false;
    }
 }

The master codes below works, and I would be able to get the coordinates String from the slave.

//master

int gpsSlavePin = 49;
const int GPS_LENGTH = 29; 

void setup () {
  pinMode(gpsSlavePin, OUTPUT);
  digitalWrite(gpsSlavePin, 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);
   
  Serial.begin(115200);
  Serial.println("Master initialized");
  
  getGpsCoordinates();
}  // end of setup

void loop () {
}  // end of loop

//This one works. I can get the responses from the slave
void getGpsCoordinates() {
  String testString = "";
  digitalWrite(gpsSlavePin, LOW);  
  SPI.transfer(GPS_COORDINATES);  //Don't need to store this response
  digitalWrite(gpsSlavePin, HIGH);
  SPI.end();
  delay(1000);
  for(int i=0; i<GPS_LENGTH; i++) {  //GPS_LENGTH
    SPI.begin();
    digitalWrite(gpsSlavePin, LOW);    // SS is pin 49
    char response = SPI.transfer(GPS_COORDINATES);
    testString += response;
    digitalWrite(gpsSlavePin, HIGH); // disable Slave Select
    SPI.end();
  }
  Serial.print(testString);
}

Ok, that's all about it. I am not sure why do I need to disable and re-enable the SPI in the loop on the master to make it work. Perhaps, can someone please explain to me why is that so?

Secondly, I am also not sure whether this is a good practice or not. If there is a better practice to achieve the same thing let me know.

Last but not least, I hope it helps for those who are trying to send a String / array of characters from slave to the master. :slight_smile:

in spi, the master controls all of the communication.

if the slave wants to send something to the master, it needs to somehow get the master to ask for it.

Generally when the master wants to get data from the slave the master has to send a "nonsense" byte to the slave because the data comes from the slave in parallel with the data going from the master to the slave. 0 (zero) is often used as the nonsense byte.

...R

Thank you @michinyon and @Robin2

But still, why do I have to keep on restarting the SPI bus, inside a loop if I were to retrieve the complete string from the slave? Why can't I retrieve the responses normally without restarting the SPI?

I don't know why you have to "restart" the SPI, if you send 10 bytes you will get 10 bytes in return (assuming the slave code is written correctly).


Rob