Be careful with the volatile variables, you should not use them directly outside of the interrupt routines because an interrupt may occur between bytes of a multi-byte variable. Common practice is to temporarily disable interrupts while copying the volatile to another variable.
In the requestEvent() handler, only use a single Wire.write (not sure if this is still needed, at some point in time the Wire library supposedly had problems with multiple Wire.writes in requestEvent).
NEVER print to Serial in an interrupt service routine. I'm not familiar with SerialUSB, but Serial uses interrupts and interrupts are disabled inside an interrupt service routine, so potentially a full send buffer would block the code forever.
Try this code and see if it works on your hardware, I do not have a 32-bit processor to test it on:
master code:
#include "Wire.h"
#define M0_I2C_SLAVE_ADDRESS 13
#define SERIAL_BUFFER_LEN 64
#define COMMAND_LEN 4
enum {
CMD_TUNE = 169,
CMD_SERVICE = 172,
CMD_INFO = 142,
CMD_STATUS = 213
};
typedef struct __attribute__ ((packed))Command {
uint8_t cmd;
uint8_t payload[3];
} Command;
void sendCommand(Command commandStruct, uint8_t responseSize) {
Wire.beginTransmission(M0_I2C_SLAVE_ADDRESS);
Wire.write((byte *)&commandStruct, COMMAND_LEN);
Wire.endTransmission ();
if (Wire.requestFrom(M0_I2C_SLAVE_ADDRESS, responseSize) == 0) {
Serial.println("Error");
} else {
char buffer[3];
buffer[0] = Wire.read();
buffer[1] = Wire.read();
buffer[2] = Wire.read();
Serial.print("Response from M0 board: ");
Serial.print(buffer[0]);
Serial.print(buffer[1]);
Serial.print(buffer[2]);
Serial.print('\n');
}
}
void setup() {
Wire.begin();
Serial.begin(115200);
}
void loop() {
if (Serial.available()) {
uint8_t txData[SERIAL_BUFFER_LEN];
size_t bytesRead = Serial.readBytes(txData, SERIAL_BUFFER_LEN);
if (txData[0] == '1') {
Command commandTune = {
.cmd = CMD_TUNE,
.payload = {107500 % 256, (107500 / 256) % 256, 107500 / (256l * 256)}
};
sendCommand(commandTune, 3);
} else if (txData[0] == '2') {
Command commandService = {
.cmd = CMD_SERVICE,
.payload = {14, 0, 0}
};
sendCommand(commandService, 3);
} else if (txData[0] == '3') {
Command commandInfo = {
.cmd = CMD_INFO,
.payload = {0, 0, 0}
};
sendCommand(commandInfo, 3);
} else {
// do nothing
}
}
}
slave code:
//for compiling on a board without SerialUSB
#ifndef SerialUSB
#define SerialUSB Serial
#endif
#include "Wire.h"
#define M0_I2C_SLAVE_ADDRESS 13
#define COMMAND_LEN 4
enum {
CMD_TUNE = 169,
CMD_SERVICE = 172,
CMD_INFO = 142,
CMD_STATUS = 213
};
typedef struct __attribute__ ((packed)) Command {
uint8_t cmd;
uint8_t payload[3];
} Command;
volatile bool printRequest = false;
volatile bool lengthError = false;
volatile int lengthErrorBytes;
volatile bool noValidCommand = false;
volatile byte receiveBuffer[sizeof(Command)] = {0};
Command bufferedCommand = {.cmd = 0, .payload = {0, 0, 0}};
void setup() {
Wire.begin(M0_I2C_SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
SerialUSB.begin(115200);
}
void receiveEvent(int nBytes) {
if (nBytes != COMMAND_LEN) {
//flush buffer
for (int i = 0; i < nBytes; i++) {
Wire.read();
}
lengthError = true;
lengthErrorBytes = nBytes;
} else {
for (uint8_t i = 0; i < COMMAND_LEN; i++) {
receiveBuffer[i] = Wire.read();
}
printRequest = true;
}
}
void requestEvent () {
//only use a single Wire.write within requestEvent
if (bufferedCommand.cmd == CMD_TUNE) {
byte data[3] = {'y', 'o', '!'};
Wire.write(data, 3);
} else if (bufferedCommand.cmd == CMD_SERVICE) {
byte data[3] = {'f', 'o', 'o'};
Wire.write(data, 3);
} else if (bufferedCommand.cmd == CMD_INFO) {
byte data[3] = {'4', '5', '6'};
Wire.write(data, 3);
} else if (bufferedCommand.cmd == CMD_STATUS) {
byte data[3] = {'x', 'y', 'z'};
Wire.write(data, 3);
} else {
//never use print within an interrupt handler
noValidCommand = true;
}
}
void loop() {
if (printRequest) {
//copy receive buffer to bufferedCommand
//note - memcpy does not like volatile
byte* ptr = (byte*)(&bufferedCommand);
noInterrupts(); //disable interrupts while accessing volatile variables
for (size_t i = 0; i < sizeof(Command); i++) {
*ptr++ = receiveBuffer[i];
}
printRequest = false;
interrupts();
SerialUSB.print("command: ");
SerialUSB.print(bufferedCommand.cmd, DEC);
SerialUSB.print('\n');
SerialUSB.print("Payload: ");
SerialUSB.println(bufferedCommand.payload[0] + bufferedCommand.payload[1] * 256l + bufferedCommand.payload[2] * 256l * 256, DEC);
}
if (lengthError){
lengthError = false;
SerialUSB.print("Length error: ");
SerialUSB.println(lengthErrorBytes, DEC);
}
if (noValidCommand) {
noValidCommand = false;
SerialUSB.println("No valid command!");
}
}