I'm trying to save GPS coordinates to an EEPROM.
In this example I feed it the latitude of 56060066 as the argument float x
void writeFloat(unsigned int addr, float x)
{
byte seriesOfBytes[3];
*((float *)seriesOfBytes) = x;
// Write all four bytes.
for(int i = 0; i < 4; i++)
{
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Write byte to EEPROM
Serial.println(seriesOfBytes[i],BIN); // Debug line
}
}
I'm expecting to receive the following four bytes from the serial print:
00000011
01010111
01101000
10100010
Instead I'm getting these:
00101000
11011010
01010101
01001100
I've tried changing all sorts of parameters, but can't seem to find the problem. Can anyone spot the issue?
4 Answers 4
If you were to enable the display of warnings you would most likely see it complain about "dereferencing type-punned pointer will break strict-aliasing rules" which is what you are doing here.
Basically you are trying to cast an array of four 8-bit values which can have any alignment they like (byte alignment) to a 32-bit float value which needs 4-byte alignment. And the two just don't mesh.
Instead you need to work the other way around - cast a type that has smaller alignment requirements over the type that has larger requirements.
So instead of getting 4 bytes and trying to fill them as a float you get a float and read it as 4 bytes:
byte *b = (byte *)&floatVal;
Then you can access b[0] to b[3] quite happily.
Another way to do it is with a union:
union {
float fval;
byte bval[4];
} floatAsBytes;
floatAsBytes.fval = floatVal;
EEPROM.write(0, floatAsBytes.bval[0]);
EEPROM.write(1, floatAsBytes.bval[1]);
EEPROM.write(2, floatAsBytes.bval[2]);
EEPROM.write(3, floatAsBytes.bval[3]);
Also, in your code, you only allocated enough room for 3 bytes, not 4...
-
Thanks. I just tried the union you suggested, but it's showing the same result! Compiler is not showing any warnings. Would you expect the serial print with BIN parameter to show the correct result?blarg– blarg2015年10月12日 17:25:39 +00:00Commented Oct 12, 2015 at 17:25
-
@blarg Have you considered that maybe you are expecting the wrong values? Where did you get those values from?Majenko– Majenko2015年10月12日 17:31:59 +00:00Commented Oct 12, 2015 at 17:31
You are getting exactly what you said you want. 4 bytes representing the number in float format.
What you are expecting to get, is 4 bytes representing the number in big-endian integer format.
Using Majenkos solution for casting, you need to change the types and reverse the for
loop for big-endinaness.
I also used int32_t
because the size of the int
is 16-bit (2-byte) on the Arduino Uno (and other ATMega based boards) and on the Arduino Due it's 32-bit (4-byte). Its better to define the exact int
in this case.
union {
int32_t ival;
byte bval[4];
} int32AsBytes;
void writeInt32(unsigned int addr, int32_t x)
{
int32AsBytes.ival = x;
for(int i = 3; i >= 0; i--) // reverse for big-endian
{
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Write byte to EEPROM
Serial.println(int32AsBytes.bval[i], BIN); // Debug line
}
}
This mucking around with casts and arrays is far too complex.
See: Reading and Writing Data Structures to EEPROM
The library EEPROMAnything.h will accomplish this.
EEPROMAnything.h
The library just consists of this file. Save into your "libraries" folder under the folder name EEPROMAnything
.
#include <Arduino.h> // for type definitions
#include <EEPROM.h>
template <typename T> unsigned int EEPROM_writeAnything (int ee, const T& value)
{
const byte* p = (const byte*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <typename T> unsigned int EEPROM_readAnything (int ee, T& value)
{
byte* p = (byte*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
Example code
#include <EEPROM.h>
#include <EEPROMAnything.h>
void setup ()
{
Serial.begin (115200);
Serial.println ();
float foo = 56060066;
EEPROM_writeAnything (0, foo);
float bar;
EEPROM_readAnything (0, bar);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
Output from above
56060064.00
Note you don't get exactly the same number back. This is because of the precision of 4-bytes floats.
After re-reading the question I see you are using I2C EEPROM. That changes the answer a bit, but you can see an adaptation of that library for I2C here. The general idea is the same though.
i2c_eeprom_write_byte(0x57, addr, myFloat.bytes[i]); // Write byte to EEPROM
Your code seems to be writing all of the bytes to the same address, I don't know how that could work.
union Float {
float m_float;
uint8_t m_bytes[sizeof(float)];
};
float pi,pi1;
uint8_t bytes[sizeof(float)];
uint8_t bytes1[sizeof(float)];
Float myFloat;
#include <Wire.h>
#define disk1 0x50 //Address of 24LC256 eeprom chip
void setup(void)
{
unsigned int address = 0;
Serial.begin(9600);
Wire.begin();
pi = 300.78;
Serial.println("*******************************************");
Serial.println("pi = "+ String(pi));
Serial.println("***** Conversion by using type casting *****");
*(float*)(bytes) = pi;
for(int i=0;i<4;i++) Serial.println( bytes[i]);
Serial.println("*******************************************");
for( int i=0;i<4;i++){
writeEEPROM(disk1, address, bytes[i]);
address=address+sizeof(bytes[i]);
}
address=0;
for( int i=0;i<4;i++){
bytes1[i]=(readEEPROM(disk1, address));
address++;
Serial.println(bytes1[i]);
}
Serial.println("************Conver Byte to Float*************************");
pi1 = *(float*)(bytes1);
Serial.println("pi = "+String( pi1));
Serial.println("********* Conversion by using union *********");
myFloat.m_float = pi1; // assign a float to union
Serial.println("myFloat.m_Float = "+String( myFloat.m_float));
Serial.println("myFloat.m_Bytes = "+String(myFloat.m_bytes[0])+String(myFloat.m_bytes[1])+String(myFloat.m_bytes[2])+String(myFloat.m_bytes[3])); // get the bytes
}
void loop(){}
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data )
{
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.send(data);
Wire.write(data);
Wire.endTransmission();
delay(5);
}
byte readEEPROM(int deviceaddress, unsigned int eeaddress )
{
-
Hope This could help. Just done a fews min ago. good luckNisit Wattanasri– Nisit Wattanasri2019年07月21日 07:53:11 +00:00Commented Jul 21, 2019 at 7:53
-
Please add a few sentences to accompany the code. It may help those who are trying to understand it.MichaelT– MichaelT2019年07月21日 11:33:53 +00:00Commented Jul 21, 2019 at 11:33