SPI and push-button code

Hello everyone,

I am trying to send data from an master Arduino Uno board to a slave Arduino Uno board. I found a great SPI_template header file online that should allow me to send any kind of template struct that I want to send. Here is the code for the header file:

Template

#include <Arduino.h>

template 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;
}

template 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;
}

template unsigned int SPI_readAnything_ISR(T& value)
{
byte p = (byte) &value;

unsigned int i;

*p++ = SPDR;

for(i = 1; i < sizeof value; i++)
*p++ = SPI.transfer(0);

return i;

}

My goal is to be able to send the struct using SPI but I would like to determine when to send the data using a simple push-button. In order to test my code, I included several Serial.println statements in the slave code. But the Serial.println statements are not working, nothing appears on the Serial Monitor before or after I press the push-button. I am using an interrupt for the push-button. Any ideas or recommendations on how to implement an external push-button that start an SPI communication?

Here is my master code:
// Information: This program is being used to test a push button, the action
// of pressing the push button should enable the SPI transfer of data.

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

typedef struct myStruct {
int a;
float b;
long c;
};

// This struct object is used to store and transfer the data values
// that the programmer wishes to use with SPI for the 'on' button.
myStruct structComm;

// This int variable will be used for the push button, Button1
// corresponds to 'send' button.
int Button1 = 2;

// The button press will switch the state.
int toggle_on;

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

pinMode(10, OUTPUT);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8);

structComm.a = 44;
structComm.b = 30.0;
structComm.c = 12;

// This code initializes the push-button pin as an input.
pinMode(Button1, INPUT);

attachInterrupt(digitalPinToInterrupt(Button1), pin_ISR1, RISING);
}

void loop() {
if (toggle_on == 1) {
digitalWrite(SS, LOW);
SPI_writeAnything(structComm);
digitalWrite(SS, HIGH);
}
}

// This interrupt function is for the push button.
void pin_ISR1()
{
static unsigned long last_interrupt_time1 = 0;

unsigned long interrupt_time1 = millis();

// After a certain amount of time, it will toggle on.
if (interrupt_time1 - last_interrupt_time1 > 200)
{
toggle_on = 1;
}

last_interrupt_time1 = interrupt_time1;
}

Here is the slave code:

// Information: This program is being used to test a push button, the action
// of pressing the push button should enable the SPI transfer of data.

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

typedef struct myStruct
{
int a;
float b;
long c;
};

volatile myStruct structComm;
volatile bool haveData = false;

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

pinMode(MISO, OUTPUT);

// This code turns on the SPI in slave mode.
SPCR |= _BV(SPE);

SPI.attachInterrupt();
}

// This void function acts just like the main function.
void loop() {
if (haveData) {
Serial.println();
Serial.println("SPI test");
Serial.println(structComm.a);
Serial.println(structComm.b);
Serial.println(structComm.c);
Serial.println();

delay(2500);

haveData = false;
}
}

// This is a SPI interrupt routine function for the green go button.
ISR(SPI_STC_vect) {
SPI_readAnything_ISR(structComm);
haveData = true;
}

I am using an interrupt for the push-button.

Why? The SPI communication is already interrupt driven, so the Arduino has nothing to do but poll the switch.

Resetting toggle_on to 0 at some point seems like a good idea.

Declaring toggle_on to be volatile seems like a good idea, because it is MANDATORY.

Hello PaulS,

Thanks for the reply. I am still learning about SPI. Looking at the SPI template, the slave code has an interrupt routine, do you recommend I use that interrupt routine for the green push button? Should I declare an interrupt function in the setup of the slave?

Should I directly connect the green push button to the slave board?

Thanks,
David

do you recommend I use that interrupt routine for the green push button?

No. I do not recommend that you use interrupts to read switches pushed by humans.

Should I directly connect the green push button to the slave board?

You should explain why you need a switch at all.

Hi Paul,

I am a member of a team of students that is attending an engineering competition. The competition consists of building a robot and the rules state that we have to include a 'green go' push-button that is clearly visible.

The robot has to complete a variety of tasks including digital input from sensors and driving some motors. The team and I decided to use two Arduino boards, the master will interface with the sensors and the slave will drive the motors.

I would like to use SPI to send a integer variable from the master to the slave as soon as I press the push button. With that int variable, the slave will execute the working motor code.

I found the SPI template code that I can customize and send any variables using SPI. I just don't understand how to incorporate the push button yet. I am watching some videos online about SPI right now.

Any advice?

Thanks Paul for the replies and your advice.

David

Any advice?

So, the master should read the state of the switch, and send something to the slave when the switch changes state.

The something that it sends should be a single byte. There is no reason to use a complex process that can send a struct containing different kinds of data, when all you want to say is "Hey, slave, break's over. Get to work".

Look at the state change detection example, and SPI.write() and/or SPI.transfer().

Hi Paul,

I have been reading the some information on SPI.transfer and I have another question. I understand for the most part, what functions and code I need to include on the master. But I am not sure what code I should include on the slave code.

Right now, I am just trying to send a byte from the master to the slave. In the slave, I want to use the byte with an if statement to serial print statements to the console.

Here is my code so far:
Master:
#include <SPI.h>

// This int variable will be used for the push button, Button1
// corresponds to 'send' button.
int Button1 = 2;

// This is the slave select pin.
const int slavePin = 10;

// The button press will switch the state.
volatile int toggle_on;

SPISettings settings(14000000, MSBFIRST, SPI_MODE1);

const byte masterG = 0x07;

void setup() {
// This code enables the slave communication.
pinMode(slavePin, OUTPUT);

SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8);

// This code initializes the push-button pin as an input.
pinMode(Button1, INPUT);

attachInterrupt(digitalPinToInterrupt(Button1), pin_ISR1, FALLING);
}

void loop() {
SPI.beginTransaction(settings);

if (toggle_on == 1) {
digitalWrite(slavePin, LOW);
SPI.transfer(masterG);
digitalWrite(slavePin, HIGH);
}

toggle_on == 0;

SPI.endTransaction();
}

// This interrupt function is for the push button.
void pin_ISR1()
{
static unsigned long last_interrupt_time1 = 0;

unsigned long interrupt_time1 = millis();

// After a certain amount of time, it will toggle on.
if (interrupt_time1 - last_interrupt_time1 > 200)
{
toggle_on = 1;
}

last_interrupt_time1 = interrupt_time1;
}

Slave:
#include <SPI.h>

byte slaveReceive = 0x00;
byte masterG;

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

pinMode(MISO, OUTPUT);

SPI.attachInterrupt();
}

// This void function acts just like the main function.
void loop() {
if (slaveReceive == 0x07) {
Serial.println();
Serial.println("SPI test");
Serial.println();
Serial.println("Forward command received.");

delay(2500);
}
}

// This is a SPI interrupt routine function for the green go button.
ISR(SPI_STC_vect) {
slaveReceive = SPI.transfer(masterG);
}

Any advice? What am I missing on the slave unit?

Thanks,
David

Hi Paul,

I was able to fix the issue.

Thanks for your advice and help,
David