Maintain variable identity between MasterWriter SlaveReceiver?

Hello!

I'm somewhat quite desperate with this project. The deadline is pretty much now so sorry if I'm hasty or unpolite. :,D

So, I want to send multiple strings, integers and floats from an Arduino Uno Master to a nodemcu slave.
The main idea of the project is that the Arduino is going to take input of multiple temperature sensors to control the temperature of an animal physiology experiment. The nodemcdu (ESP8266mod) is going to receive this temperature data from Arduino, log then into google drive and, if some parameters is wrong, send a e-mail to me.

I believe I got almost everything figured out. I know the wiring, how to log the data, how to send an email, how to send convert float to char and back to float, etc.

I have seen multiple tutorials and such (all of them coping from the same example...) and all of then deal with a simpler problem that deals with passing only one or two values with different byte sizes.(Tutorial example)
I need an example code that sends multiple variables. All of them will be less than 8byte in size.

The pseudocode of what I want to do is something as follows:

->>Arduino Master Writer

If one minute has passed {
Receive input from 8 sensors
Makes decisions based on said input

Then
Wire.beginTransmission(<adress>); 
Wire.write(<date>);        // String (ex. Set22)
Wire.write(<hour>);        //String (ex. 14h33)
Wire.write(<Val sensor 1>);        // float (I have read that it is easier if I convert it to string. Most likelly will do so)
Wire.write(<Val sensor n>);        // float (ex. 23.444)
Wire.write(<Val sensor 8>);        // float (ex. 30.222)
Wire.write(<state of equipment 1>);        //bolean (wheter the equipment is on or off)
Wire.write(<state of equipment n>);        //bolean (wheter the equipment is on or off)
Wire.write(<state of equipment 4>);        //bolean (wheter the equipment is on or off)
Wire.write(<Val light sensor >);        //int  (ex. 50000)
Wire.endTransmission();    // stop transmitting
}

->>Nodemcu SlaveReceiver

void setup() {
  Wire.begin(<adress>);                // 
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {
 
}


void receiveEvent(int howMany) {

char a = Wire.read(<date>); 
char b = Wire.read(<hour>);        
float c = Wire.read(<Val sensor 1>);
etc
int n = Wire.read(<Val light sensor >);

upload2Drive(a,b...,n){}
shitHappened(a,b...n){}  //sends email.
}

Can anybody please provide any example of something like that?
I've tried multiple search strings on google and could find nothing!

-Edit-
The main question here, I believe, is how to maintain the identity of each variable among the two boards. The tutorials counts bytes, (e.g., the last byte is another variable), but I needed something that would identify positions (e.g. the first value(<=8bytes) is the first variable, the second (<=8bytes) is the second etc...

Rola

All of them will be less than 8byte in size.

Are you aware that the write() method (that takes one argument) writes ONE byte?

Your code that reads the data written to the I2C pins MUST know the order, and size, of each bit of data. There is nothing that makes that happen for you.

Thank you very much for your time and reply!

Are you aware that the write() method (that takes one argument) writes ONE byte?

I was not aware of that. I think therefore that I must have misunderstood the documentation and tutorial badly!

In the the tutorial, the master apparently sends a string 5 bytes long:

void loop() {
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte
  Wire.endTransmission();    // stop transmitting

  x++;
  delay(500);
}

So is this information then sent a character at a time (like "x", " ", "i", "s" and" ")?

Your code that reads the data written to the I2C pins MUST know the order, and size, of each bit of data. There is nothing that makes that happen for you.

I'm sorry, I do not think I understand. So given and event of information transmitting, the values for each variable would mend together and the therefor e I need to provide to receiver the order and sizes of variables so it can "deconstruct" the long melding into the original values?

Thank you very much.

@OP

Check and re-check if NodeMCU works as a slave in the I2C Bus with UNO?

If not, use Software UART Communication.

The write() method handles strings properly, but the Wire class isn't really intended for sending strings.

float pi = 3.14159;
Wire.write(pi);

will NOT send the value of pi as you expect.

float pi = 3.14159;
Wire.write(pi, sizeof(float));

Will send all 4 bytes of the value.

Of course, you need to make sure that the receiver expects 4 bytes, and reads all 4 of them and uses all 4 of them in the correct order to reconstruct the float.

float pi = 3.14159;

Wire.write(pi, sizeof(float));



Will send all 4 bytes of the value.

Unfortunately, compilation error!

no matching function for call to 'TwoWire::write(float&, unsigned int)'

Unfortunately, compilation error!

So, fix it.

Wire.write(&pi, sizeof(float));

So, fix it.

The fix is:

Wire.print(pi);

or

union
{
   float pi;
   byte myData[4];
}data;

data.pi = 3.14159;
Wire.write(data.myData, sizeof(data.myData));

The fix is:
Code: [Select]

Wire.print(pi);

Bullshit.

The write() and print() methods do VERY different things.

There is no reason to create a union when the compiler is perfectly capable of treating a float as an array of 4 bytes.

Bullshit.

Contradicts with the practical result.
Screenshot of the Slave in response to Wire.print()
smfloat.png

Master-Code

#include<Wire.h>
bool flag1 = false;
byte x;

float pi = 3.14159;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
  Wire.beginTransmission(0x68);
  Wire.print(pi, 5);
  Wire.endTransmission();
  delay(1000);    //test interval
}

Slave-Code

#include<Wire.h>
bool flag1 = false;
char myData[20] = "";

void setup()
{
  Serial.begin(9600);
  Wire.begin(0x68);  ////UNO-Slave Address
  Wire.onReceive(receiveEvent);
}

void loop()
{
  if (flag1 == true)
  {
    Serial.print("Received from MEGA is: ");
    Serial.println(myData);
    flag1 = false;
  }
}

void receiveEvent(int howMany)
{
  for (int i = 0; i < howMany; i++)
  {
    myData[i] = Wire.read(); //read the received byte
  }
  flag1  = true;
}

smfloat.png

PaulS:

Wire.write(&pi, sizeof(float));

Also, compilation error?

no matching function for call to 'TwoWire::write(float*, unsigned int)'

"Galileo Galilei says that he does not guess; he experiments and then predicts."

If you write the receiver to expect a string, then print() can be used to send the value. But, that is NOT the normal way that I2C is used. I2C is used to write values, not strings, to other devices.

PaulS:
If you write the receiver to expect a string, then print() can be used to send the value. But, that is NOT the normal way that I2C is used. I2C is used to write values, not strings, to other devices.

It is not forbidden; it is legal.