EEPROM.put 2 empty bytes when passing struct with int

Hello. I am writing EEPROM wrapper which would write data in chunks. My data structure looks as following:

struct my_data{
  uint8_t data1 = 5;
  uint8_t data2 = 15;
  int data3 = 512;
  int data4 = 256;
}configuration;

uint8_t is 1 byte long and int is 4 byte long, so my struct should be 10 bytes long in total.

I used this forum thread as a reference to write my EEPROM write function:
https://www.arduino.cc/playground/Code/EEPROMWriteAnything

My function looks like:

   template <class T> 
   void write_to_chunk_struct(size_t chunk, const T& value)
   {
    EEPROM.put(chunk,value);
    Serial.println("printing after put struct");
    for(int i = 0; i<size_of_eeprom;i++){
        Serial.println(EEPROM.read(i));
    }
   }

After initialsing my eeprom class object, I am calling this function in my main as following:

eeprom_rotate eeprom1(50,10);//this is a class constructor
eeprom1.write_to_chunk_struct(0,configuration);

After writing, I read the EEPROM And the result is not quite as expected:

Value inside EEPROM 0 = 5
Value inside EEPROM 1 = 15
Value inside EEPROM 2 = 0 // why this empty byte?
Value inside EEPROM 3 = 0// why this empty byte?
Value inside EEPROM 4 = 0
Value inside EEPROM 5 = 2
Value inside EEPROM 6 = 0
Value inside EEPROM 7 = 0
Value inside EEPROM 8 = 0
Value inside EEPROM 9 = 1
Value inside EEPROM 10 = 0
Value inside EEPROM 11 = 0

As you can see, the first 2 values ( uint8_t ) are correct. But for some reason, before int, it leaves an empty gap of 2 bytes. I would expect my EEPROM to look something like:

Value inside EEPROM 0 = 5
Value inside EEPROM 1 = 15
//The following are 4 bytes for INT
Value inside EEPROM 2 = 0
Value inside EEPROM 3 = 2 //represents 512
Value inside EEPROM 4 = 0
Value inside EEPROM 5 = 0
//The following are 4 bytes for INT
Value inside EEPROM 6 = 0
Value inside EEPROM 7 = 1 //represents 256
Value inside EEPROM 8 = 0
Value inside EEPROM 9 = 0

In Arduino with 8bit AVR...

int is 2 bytes. :wink:

Hello. In my case (programming ESP32) the int is definately 4 bytes. However, even if it was 2 bytes, it wouldnt explain why there are 2 empty bytes in EEPROM before the int.

Some processors enforce memory alignment.
See if a packing pragma works.

Oops, ESP32 is a virtual EEPROM.
What if I commit after put?
Without this, it wouldn't actually be written, right?

EEPROM.put(chunk, value);
EEPROM.commit(); // Add this

Since I am reading the EEPROM in the same function right after I am doing EEPROM.put, I think that makes no difference. But you are right, It wont actually right to EEPROM without that, if the device restarts before the commit, the data wont be there.

Print the size of the struct with and without

__attribute__ ((packed))

Spot the difference

Please can you explain a little further what you mean by that?

struct  __attribute__ ((packed)) my_data

the struct occupies the expected 10 bytes because the data has been packed together to occupy contiguous memory locations

struct  my_data

the struct occupies 12 bytes because it has been aligned in memory to make access faster at the expense of using more memory

Yes you are right. I am now unsure which method I should be using. I assume using packed struct would be more convenient since I know exactly where each data is placed based on the variables size. Because when I use EEPROM.get method to retrieve data from the EEPROM, I must know the exact length of the struct to be able to retrieve the correct data.

Why do you actually need to know where each variable is located when you can just get() the struct when needed ?

If the pack is not explicitly specified, optimise to merge the first two 1 byte of the struct (data is 8+8 bits and empty 16 bits) in an attempt to manage data in 32 bits.
Therefore, 32 bits(byte+byte+padding) + 32 bits(int) + 32 bits(int) occupy the memory.
If don't do this, the latter int is divided into two words and stored, which reduces performance.

Is the above interpretation correct?

Yes you are right I think. I have created an identical struct

struct my_data2{
  uint8_t data1;
  uint8_t data2;
  int32_t data3;
}configuration2;

And able to read the data without packing as following:

template <class T> 
   void read_to_struct(size_t chunk, const T& value)
   {
    EEPROM.get(chunk,value);
    EEPROM.commit();
   }

And then reading the data back as following:

  Serial.print("struct after read = ");
  Serial.println(configuration2.data1);
  Serial.println(configuration2.data2);
  Serial.println(configuration2.data3);

However, I am unsure about one more thing though. This is slightly out of topic but maybe you would know why do I must declare my struct as a reference in my function declaration?

const T& value

Yes perhaps that is another method, But I have managed to figure it out what was the issue. attribute ((packed)) packs the data as expected

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.