We are almost there.
We don't use readable ASCII for the I2C bus.
A fixed size of binary data is achieved this way:
[...]
or with a struct:
I like the idea of having a struct of fixed size, I somehow forgot that there are structs in C.
if( howMany == 18) // expecting 18 bytes, ignore everything else
That's a very good check. I will use that too.
Calling Wire.write() outside requestEvent() is undefined behaviour. Please follow the examples.
I have a hard time understanding how an API call can be undefined. A callback/handler is a function call based on an interrupt; a function call is a jump instruction; why shouldn't it be possible to execute certain instructions outside of that?
Why would you use a while-statement ? How many times do you want to read the package of data ? [...] That's how the Wire library examples receive I2C input. [...] I know ! and it is really bad.
I think we all agree that the provided examples and built-in libraries might be not optimal. I mean, if you follow the API calls, you will eventually find out that any read operation on the I2C bus is blocking..
Anyway, back to the task. Since the nRF is a 32-Bit CPU, I defined my struct to fit within 32 bits, otherwise another 32 bits would be allocated.
typedef struct Command{
uint8_t cmd : 8;
uint32_t payload : 24;
}Command;
Adjusted 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 Command{
uint8_t cmd : 8;
uint32_t payload : 24;
}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
};
sendCommand(commandTune,3);
}else if(txData[0]=='2'){
Command commandService = {
.cmd = CMD_SERVICE,
.payload = 14
};
sendCommand(commandService,3);
}else if(txData[0]=='3'){
Command commandInfo = {
.cmd = CMD_INFO,
.payload = 0
};
sendCommand(commandInfo,3);
}else{
// do nothing
}
}
}
Slave code:
#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 Command{
uint8_t cmd : 8;
uint32_t payload : 24;
}Command;
volatile uint8_t printRequest = 0;
volatile Command bufferedCommand = {.cmd = 0, .payload = 0};
void setup() {
Wire.begin(M0_I2C_SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
SerialUSB.begin(115200);
}
void receiveEvent(int nBytes){
//SerialUSB.println("Callback receiveEvent()!"); // still gets called twice with each transmission
if(nBytes != COMMAND_LEN){
SerialUSB.print("Length error: ");
SerialUSB.println(nBytes,DEC);
}else{
byte cmdBuffer[COMMAND_LEN];
for (uint8_t i=0; i<COMMAND_LEN; i++){
cmdBuffer[i] = Wire.read();
}
bufferedCommand.cmd = cmdBuffer[0];
bufferedCommand.payload = (uint32_t) cmdBuffer[3] << 16;
bufferedCommand.payload |= (uint32_t) cmdBuffer[2] << 8;
bufferedCommand.payload |= (uint32_t) cmdBuffer[1];
printRequest = 1;
}
}
void requestEvent (){
if(bufferedCommand.cmd==CMD_TUNE){
Wire.write('y');
Wire.write('o');
Wire.write('!');
}else if(bufferedCommand.cmd==CMD_SERVICE){
Wire.write('f');
Wire.write('o');
Wire.write('o');
}else if(bufferedCommand.cmd==CMD_INFO){
Wire.write('4');
Wire.write('5');
Wire.write('6');
}else if(bufferedCommand.cmd==CMD_STATUS){
Wire.write('x');
Wire.write('y');
Wire.write('z');
}else{
SerialUSB.println("No valid command!");
}
}
void loop() {
if(printRequest){
SerialUSB.print("command: ");
SerialUSB.print(bufferedCommand.cmd, DEC);
SerialUSB.print('\n');
SerialUSB.print("Payload: ");
SerialUSB.println(bufferedCommand.payload, DEC);
printRequest = 0;
}
}
This works quite well expect for the fact that the callback receiveEvent() still gets called twice at each transmission for some reason. This makes the behaviour sometimes a bit buggy. In one off the calls, nBytes is equal to COMMAND_LEN and everything works as expected. In the other call, nBytes is equal to zero and the error message pops up.