How to send a struct from Slave to Master via I2C

I have had good success sending 31 bytes of char data back and forth between my NodeMCU and Nano (thanks to this forum and Mr. Gammon). But now I wish to send the sensor data back from the slave in the current format which is a 30 byte struct. My project is bidirectional so the Easy Tranfer and I2C Anythiong libraries did not have readily available and (working) examples.to use . I really would hope I could use my existing format . How can the Master convert the data back into the struct

This code works on the slave end sending to the Master:

Wire.write((byte *)&my_struct1, sizeof my_struct);

I can see the data on the master with this code:

while(Wire.available()){   

     char c = Wire.read();

     Serial.print(c);

     ++i;
     }

All is good except now I want the data to go back into the struct format establised on the slave:

typedef struct {
  int W;
  int X;
  int Y;
  int Z;
  }
B_t;
B_t my_struct2;

How can the Master convert the data back into the struct ? Especially when the contents of the struct is not iterable .I've searched everywhere and have not found an example where the code for the master receiving a struct from a slave is shown. Only the slave sending it.

buckstucky:
I have had good success sending 31 bytes of char data back and forth between my NodeMCU and Nano (thanks to this forum and Mr. Gammon). But now I wish to send the sensor data back from the slave in the current format which is a 30 byte struct. My project is bidirectional so the Easy Tranfer and I2C Anythiong libraries did not have readily available and (working) examples.to use . I really would hope I could use my existing format . How can the Master convert the data back into the struct

This code works on the slave end sending to the Master:

Wire.write((byte *)&my_struct1, sizeof my_struct);

I can see the data on the master with this code:

while(Wire.available()){   

char c = Wire.read();

Serial.print(c);

++i;
    }



All is good except now I want the data to go back into the struct format establised on the slave:


typedef struct {
 int W;
 int X;
 int Y;
 int Z;
 }
B_t;
B_t my_struct2;




How can the Master convert the data back into the struct ? Especially when the contents of the struct is not iterable .I've searched everywhere and have not found an example where the code for the master receiving a struct from a slave is shown. Only the slave sending it.

simple really :

use this same code you posted!

while(Wire.available()){   

     char c = Wire.read();

     Serial.print(c);

     ++i;
     }

except instead of using "Serial.print(c);"

you would do replace it with something like this:

switch(i){
   case 0:
      my_struct2.W = c;
   break; 

   case 1:
      my_struct2.X = c;
   break;  

   case 2:
      my_struct2.Y = c;
   break; 

   case 3:
      my_struct2.Z = c;
   break;  
}

the only this I would be careful about is making sure I reset 'i' and preferable sent some sort of 'header' before sending the actual struct so that the Master 'knows' how to interpret the incoming data

maybe also have a look at the Serial Input Basics to get some more ideas on how to package to incoming data.

I can see the data on the master with this code:

The master should be requesting data with a Wire.requestFrom(slaveID, n_bytes) command.

Then if you want the returned data in struct called my_struct2 which is identical to the sending struct you would use

Wire.readBytes((byte*)&my_stuct_2, n_bytes);

This code works on the slave end sending to the Master:

Wire.write((byte *)&my_struct1, sizeof my_struct);

Is this slave code being sent from an onRequest() or onReceive() handler?

You should be posting working example code instead of snippets.

Thanks for your response, I thought you had the fix for my issue but I dont think I have implemented your changes correctly. Here is the full code:

Master:

#include <Wire.h>
char t[16];
int rcmd = 0;
//struct OUT from master to slave
typedef struct {
 int A;
 int B;
 int C;
 int D;
  }
A_t;
A_t my_struct1;

// struct IN to Master from slave
typedef struct {
  int W;
  int X;
  int Y;
  int Z;
  }
B_t;
B_t my_struct2;

void setup() {
 Serial.begin(9600); /* begin serial for debug */
 Wire.begin(D1,D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}
void loop() {
  //  if ( Serial.available() )
  //  {
 //    rcmd = (Serial.read());
 //    digitalWrite(16,LOW);
  //  }

   rcmd=1;
   my_struct1.A = 1;
   my_struct1.B = 2; 
   my_struct1.C = 3;
   my_struct1.D = 4;

//  Write Data  
 Wire.beginTransmission(8); /* begin with device address 8 */
 Wire.write ((byte *) &my_struct1, sizeof my_struct1);  /* sends hello string */
 Wire.endTransmission();    /* stop transmitting */
 
//  Read Data
 Wire.requestFrom(8, 16); 
// while(Wire.available()){   
//     char c = Wire.read();
//     Serial.print(c);

Wire.readBytes((byte*)&my_struct2, sizeof my_struct2);
Serial.print("Data from Slave=");
Serial.print(my_struct2.W);
Serial.print(my_struct2.X);
Serial.print(my_struct2.Y);
Serial.println(my_struct2.Z);
     }

Slave:

#include <Wire.h>

int ledPin = 8;        // LED pin for   Charge status indicator
int data_ret =0;
//struct IN to slave from master
typedef struct {
  int A;
  int B;
  int C;
  int D; 
}
A_t;
A_t my_struct1;

//struct OUT from slave to master
typedef struct {
  int W;
  int X;
  int Y;
  int Z;
  }
B_t;
B_t my_struct2;

void setup() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent); /* register receive event */
 Wire.onRequest(requestEvent); /* register request event */
 Serial.begin(115200);           /* start serial for debug */
}

void loop() {

//if (Serial.available())
//{
//  data_ret=(Serial.read());
//}
data_ret=1;  
my_struct2.W =5+data_ret;
my_struct2.X =6+data_ret;
my_struct2.Y =7+data_ret;
my_struct2.Z =8+data_ret;
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
 while (0 <Wire.available()) {
    char c = Wire.read();      /* receive byte as a character */
    Serial.print(c);           /* print the character */
  }
 Serial.println();             /* to newline */
}

// function that executes whenever data is requested from master
void requestEvent() {
 //Wire.write("111222333444555666777888999");  /*send string on request */
 Wire.write((byte *)&my_struct2, sizeof my_struct2);  /*send string on request */
}

The monitor on the Slave is blank but the out put from the master is scrolling :

.
.
.
“Data from Slave=00-1-1”
.
.
.

I expected to see “1234” on the slave monitor and “Data from slave=6789” on the master.
Any ideas ? It seem like there is a mryiad ways to do this but im having trouble making the struct method work. I wanted to ask the “experts” before I hoist the white flag. The previous case method did not work as well. But thanks for respond too!

@OP

Please, provide a typical numerical example of your conglomerate struct composed of total 30-byte data that you want to send from NANO-Slave to Node-Master using I2C Bus.

Its in the sketch and its actually 16 bytes both ways. The slave sends "6,"7","8","9" (int) to the master if a "1 " is sent on the serial port.

The master sends "1","2","3","4" (int) to the slave.

Your test code works between two Nanos.

There is some issue of Node MCU master to Nano slave. There are possible issues of voltage levels(5/3.3v), pullups, and the need for clock stretching on the ESP. The web is full of discussions.

That said, I could not get the sketches working between a Wemos D1 and a Nano with what I tried.
I even changed declarations on the ESP from int to int16_t and tried with an 8 byte request in case there were issues with the two platforms.