0

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?

asked Oct 12, 2015 at 15:16

4 Answers 4

1

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...

answered Oct 12, 2015 at 15:53
2
  • 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? Commented 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? Commented Oct 12, 2015 at 17:31
2

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
 }
}
answered Oct 12, 2015 at 19:09
1

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.

answered Oct 12, 2015 at 20:41
-1
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 ) 
{
MichaelT
8873 gold badges8 silver badges22 bronze badges
answered Jul 21, 2019 at 7:51
2
  • Hope This could help. Just done a fews min ago. good luck Commented 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. Commented Jul 21, 2019 at 11:33

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.