A Forum member asked for help to send a floating point number (say, 3.45) from NodeMCU to MEGA using I2C Bus. I decided to use union structure to extract the binary32 formatted 4-byte data items for the given float number; unfortunately, I am getting 0s for all the individual bytes. However, I have solved the problem using pointer method. Please, see the codes given below.
I would highly appreciate your effort to detect my fault or the fault of the NodeMCU or the fault of the compiler.
union
{
float x = 3.45;
byte myArray[4];
} myData;
float y = 3.45;
byte x0, x1, x2,x3;
void setup()
{
Serial.begin(115200);
byte *ptr;
ptr = (byte*) &y;
x0 = *ptr;
ptr++;
x1 = *ptr;
ptr++;
x2 = *ptr;
ptr++;
x3 = *ptr;
}
void loop()
{
for (int i = 0; i < 4; i++)
{
Serial.print(myData.myArray[i]); //shows: 00 00 00 00; expecting: CD CC 5C 40 (LS-byte first)
Serial.print(" ");
}
Serial.println();
Serial.print(x0, HEX); Serial.print(x1, HEX); //shows: 40 5C
Serial.print(x2, HEX); Serial.println(x3, HEX); //shows: CC CD
delay(1000);
}


I would start with a
myData.x = 3.45;
in setup (where I would put the whole code anyway).
I don't trust your fishy initialization of myData.
What is with the initialization of myArray?
Why do you use a different float (y) with the "pointer method"?
I'd also double check sizeof(float). Could it default to 8 bytes on NodeMCU?
You may be seeing undefined behavior. Reading from a member of a union after assigning a different member of the union is undefined behavior.
Besides, using a union just to send a float is not necessary. Just pass a pointer to the float and its size to whichever method you call to send the bytes:
float y = 3.45;
Serial.write(&y, sizeof y); // or whatever method you use to send the 4 bytes
Can be a compiler initialization of myArray to all 0?
What happens if you change the order of union's fields?
union
{
byte myArray[4];
float x = 3.45;
} myData;
Whandall:
I would start with a
myData.x = 3.45;
The above advice has solved the problem. It has also provided additional lesson on how to initialize the members of a union.
Thanks.
union
{
float x; //= 3.45;
byte myArray[4];
} myData;
void setup()
{
Serial.begin(115200);
myData.x = 3.45;
}
void loop()
{
for (int i = 0; i < 4; i++)
{
Serial.print(myData.myArray[i], HEX); //shows: the expected value: CD CC 5C 40 (LS-byte first)
Serial.print(" ");
}
Serial.println();
}

BTW: To observe steady output on the Serial Monitor, I have to put the print command in the loop() at 1-sec interval.

GolamMostafa:
The above advice has solved the problem. It has also provided additional lesson on how to initialize the members of a union.
That's an assignment statement, not an initialization. I don't have a NodeMCU to test this, but you could try:
union myUnion {
float x;
byte myArray[4];
};
myUnion myData = {.x = 3.45};
void setup() {
//
//
}
void loop() {
//
//
}
gfvalvo:
That's an assignment statement, not an initialization. I don't have a NodeMCU to test this, but you could try:
1. Initialization refers to the value that is given to the variable at the time of declaration. The variable has been declared in the global area, the initial value is assigned in the setup as this code myData.x = 3.45; is not allowed in the global area. Therefore, it is an initialization task and not an assignment task.
2. A variable can be assigned a value after its declaration in two ways regardless whether it was initialized or not, and these ways are:
(1) by the user.
(2) by the program codes.
3. Your codes work well:
union myUnion
{
float x;
byte myArray[4];
};
myUnion myData = {.x = 3.45};
void setup()
{
Serial.begin(115200);
}
void loop()
{
for (int i = 0; i < 4; i++)
{
Serial.print(myData.myArray[i], HEX);
Serial.print(" ");
}
Serial.println();
delay(1000);
}

4. If the line 'myUnion myData = {.x = 3.45};' is shifted in the setup() function, then (according to you) we are assigning a value to the variable; we are not initializing it. Correct? If I do so, the codes (CD CC 5C 40) are not generated, anyway.
union myUnion
{
float x;
byte myArray[4];
};
myUnion myData;// = {.x = 3.45};
void setup()
{
Serial.begin(115200);
myUnion myData = {.x = 3.45};
}
void loop()
{
for (int i = 0; i < 4; i++)
{
Serial.print(myData.myArray[i], HEX); //shows: 0 0 0 0
Serial.print(" ");
}
Serial.println();
delay(1000);
}



@GolamMostafa, there are a number of items in your post that are simply wrong.
GolamMostafa:
1. Initialization refers to the value that is given to the variable at the time of declaration.
The variable has been declared in the global area,
Actually, it was defined in the global area.
the initial value is assigned in the setup as this code myData.x = 3.45; is not allowed in the global area. Therefore, it is an initialization task and not an assignment task.
You said it yourself, the variable is assigned a value in setup(). It was already defined. Therefore, it is an assignment, and not an initialization.
2. A variable can be assigned a value after its declaration in two ways regardless whether it was initialized or not, and these ways are:
(1) by the user.
(2) by the program codes.
Both of those are by program code.
3. Your codes work well:
Good. As I said, I didn't have a NodeMCU to test it.
4. If the line 'myUnion myData = {.x = 3.45};' is shifted in the setup() function, then (according to you) we are assigning a value to the variable; we are not initializing it. Correct? If I do so, the codes (CD CC 5C 40) are not generated, anyway.
No, 'myUnion myData = {.x = 3.45};' inside of setup is still a definition / initialization. But, you are defining / initializing a local variable. It shadows the global myData variable that you defined above. But, the local variable goes out of scope (and existence) after setup() exits. That's why you code didn't work. Your loop function tried to print the contents of your global myData variable that you defined but did not initialize or assign. Try doing a Google search for "variable shadowing".