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?

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

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?
    – blarg
    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
    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
    }
}
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.

-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 ) 
{
2
  • Hope This could help. Just done a fews min ago. good luck 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
    Jul 21, 2019 at 11:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.