We are making progress, but I still have some issues.
But first of all, I would like to thank you all for taking your time and sharing your knowledge - much appreciated.
Let me explain the "protocol" I use. The payload from the master will (should) never exceed 16 bytes, and it could technically been padded such that it always uses 16 bytes. My I2C Stream will consist of 18 bytes:
1st byte: command (just an integer)
2nd byte: length of the payload (integer)
3th - 18th byte: payload
Here is the code of the slave. It does not do anything useful, it just returns some dummy data depending on the command and also prints out all information of interest.
#include "Wire.h"
#define M0_I2C_SLAVE_ADDRESS 13
volatile char commandBuffer [16];
volatile uint8_t command;
volatile uint8_t commandLength;
volatile uint8_t commandRequest = 0;
volatile uint8_t printRequest = 0;
enum {
CMD_CTRL_RADIO = 169,
CMD_INFO = 142,
CMD_STATUS = 213
};
void setup() {
Wire.begin(M0_I2C_SLAVE_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
SerialUSB.begin(115200);
}
void receiveEvent(int nBytes){
uint8_t i = 0;
while(0 < Wire.available()){ // why is Wire.available() necessary here? Using "nBytes" won't work.
if(i==0){
command = Wire.read();
}else if(i==1){
commandLength = Wire.read();
}else if(i>1 && i<15){
commandBuffer[i] = Wire.read();
}else{
// do nothing, prevent overflow.
}
i++;
}
commandBuffer[15] = '\0';
printRequest = 1;
}
void requestEvent (){
commandRequest = 1;
}
void loop() {
if(printRequest){
SerialUSB.print("command: ");
SerialUSB.print(command);
SerialUSB.print('\n');
SerialUSB.print("length: ");
SerialUSB.print(commandLength, DEC);
SerialUSB.print('\n');
SerialUSB.print("buffer: ");
for(int i=0;i<16;i++){
SerialUSB.print(commandBuffer[i]);
}
SerialUSB.print('\n');
printRequest = 0;
}
if(commandRequest){
SerialUSB.println("Commando Request received!");
if(command==CMD_CTRL_RADIO){
Wire.write('y');
Wire.write('o');
Wire.write('!');
}else if(command==CMD_INFO){
Wire.write('f');
Wire.write('o');
Wire.write('o');
}else if(command==CMD_STATUS){
Wire.write('4');
Wire.write('5');
Wire.write('6');
}else{
SerialUSB.println("No valid command!");
}
commandRequest = 0;
}
}
Now to the master. its code can either send any message from the serial monitor to the I2C interface, or it requests a response of 3 bytes when I type in '1', '2', or '3' in the terminal. Additionally, some random payload is transmitted.
#include <Wire.h>
#define M0_I2C_SLAVE_ADDRESS 13
#define MAX_MESSAGE_LENGTH 16
enum {
CMD_CTRL_RADIO = 169,
CMD_INFO = 142,
CMD_STATUS = 213
};
void sendCommand(uint8_t cmd, uint8_t len, uint8_t responseSize){
Wire.beginTransmission(M0_I2C_SLAVE_ADDRESS);
Wire.write(cmd);
Wire.write(len);
if(cmd==CMD_CTRL_RADIO){
Wire.write('x');
Wire.write('y');
Wire.write('z');
}else if(cmd==CMD_INFO){
// no payload
}else if(cmd==CMD_STATUS){
Wire.write('B');
}else{
Serial.println("No valid command!");
}
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[MAX_MESSAGE_LENGTH];
size_t bytesRead = Serial.readBytes(txData, MAX_MESSAGE_LENGTH);
if(txData[0]=='1'){
sendCommand(CMD_CTRL_RADIO,3,3);
}else if(txData[0]=='2'){
sendCommand(CMD_INFO,0,3);
}else if(txData[0]=='3'){
sendCommand(CMD_STATUS,1,3);
}else{
int i = 0;
Wire.beginTransmission(M0_I2C_SLAVE_ADDRESS);
while (i < bytesRead) {
Wire.write(txData[i]);
i++;
}
Wire.write('\r');
Wire.endTransmission();
}
}
}
Here is the output of my serial monitors when I send all three commands one after another:
On the slave's side, everything looks good. But the response at the master is not readable.
Issue 1
Using Wire.write() by the slave outside of the request handler seems broken. I can get valid responses when I place the writing process inside this callback.
Issue 2
Why not place the response code insithe this handler then? Well, there is something odd with the order in which the callbacks are executed. Whenever I send a command from the master, the handlers are called in this order:
receiveEvent -> requestEvent -> receiveEvent
I am not quite shure why the receiveEvent is called twice. To code I use inside the master is the same you find in many example code snipplets. The problem now is that inside the requestEvent, I do not have received all bytes yet. How to prevent this?
This would be expected when the M0 is not plugged in (unpowered). There are diodes on the input pins of almost all processors that prevent the pin from going more than about 0.7v above VCC, which in this case would be 0V since it is unpowered.
Nice to know, thank you.
