Save struct in fram

Hi guys,

I recently got my hands on a Adafruit Fram Breakout. I’m trying to store different data types using a struct. So far I have not been able to do it successfully.

There are many ressources talking about this subject but I have not seen one that give a clear and complete answer. So far I’ve tried this:

/*
  31.12.2020 
  Based on : https://arduino.stackexchange.com/questions/76951/arduino-i2c-external-32kb-fram-data-organization
*/

#include <Wire.h>
#include "Adafruit_FRAM_I2C.h"

Adafruit_FRAM_I2C fram     = Adafruit_FRAM_I2C();
uint16_t          framAddr = 0;


struct program_data {
  bool variable0;
  byte variable1;
  int variable2;
  float variable3;
  long variable4;
} data;

void setup() {
  Serial.begin(9600);
  fram.begin(0x50);

  data.variable0 = 1;
  data.variable1 = 102;
  data.variable2 = 569;
  data.variable3 = 3.14;
  data.variable4 = 100000;

  save_data(&data);
  restore_data(&data);
}

void loop() {

}

void save_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++) {
    fram.write8(i, ptr[i]);
  }
}

void restore_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++){
    Serial.print(fram.read8(i)); Serial.print(",");
  }
  Serial.println("");
}

And this is the result :

1,102,57,2,195,245,72,64,160,134,1,0,

This issue is probably the way I’m printing the struct. What should I change in order to print the values correctly ?
Ideally I would like to have it like this :

1,102,569,3.14,100000

Thanks your suggestions/ideas.

You have the source of the EEPROM library.
Take a look at it, particularly at the get and put methods.
You don’t appear to have a method to print your struct.
You could fix that.

You're just printing the bytes you saved to the FRAM. You need to print the components of the struct instead.

Thanks for your replies.

I’ve lot at the eeprom get and put examples but it didn’t really helped me.

You’re just printing the bytes you saved to the FRAM. You need to print the components of the struct instead.

I was thinking that it might be the solution but I really don’t know how to print the components of the struct.
I’ve tried saving each byte in an array and add them to get the different component of the struct like so:
I’ve tried using the technic I’ve seen in : https://forum.arduino.cc/index.php?topic=618910.0

/*
  31.12.2020
  Based on : https://arduino.stackexchange.com/questions/76951/arduino-i2c-external-32kb-fram-data-organization
*/

#include <Wire.h>
#include "Adafruit_FRAM_I2C.h"

Adafruit_FRAM_I2C fram     = Adafruit_FRAM_I2C();
uint16_t          framAddr = 0;
uint8_t temporaryArray[9];


struct program_data {
  bool variable0;
  byte variable1;
  int variable2;
  float variable3;
  long variable4;
} data;

void setup() {
  Serial.begin(9600);
  fram.begin(0x50);

  data.variable0 = 1;
  data.variable1 = 102;
  data.variable2 = 569;
  data.variable3 = 3.14;
  data.variable4 = 100000;

  save_data(&data);
  restore_data(&data);
}

void loop() {

}

void save_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++) {
    fram.write8(i, ptr[i]);
  }
}

void restore_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++) {
    //    Serial.print(fram.read8(i)); Serial.print(",");
    temporaryArray[i] = fram.read8(i);
  }
  data.variable0 = temporaryArray[0];
  data.variable1 = temporaryArray[1];
  data.variable2 = (temporaryArray[2] << 8) | (temporaryArray[3]);
  data.variable3 = (temporaryArray[4] << 16) | (temporaryArray[5] << 8) | (temporaryArray[6]);
  data.variable4 = (temporaryArray[7] << 16) | (temporaryArray[8] << 8) | (temporaryArray[9]);
  Serial.print(data.variable0); Serial.print(",");
  Serial.print(data.variable1); Serial.print(",");
  Serial.print(data.variable2); Serial.print(",");
  Serial.print(data.variable3); Serial.print(",");
  Serial.print(data.variable4); 
}

and got this :

1,102,14594,-2744.00,-24575

Still does not work but at least I have the right number of components now^^.

Is there a better way to print the components ?

Just write another function that takes a pointer to the struct and prints the contents.

I’ve lot at the eeprom get and put examples but it didn’t really helped me.

Maybe you misunderstood the code.

RedIron:
Is there a better way to print the components ?

Not really. the way you're doing it here is fine:

Serial.print(data.variable2);

You have made things overcomplicated though. Your restore function should be almost identical to your save function - just read bytes from the fram into the struct.

I should have mentionned at the beginning that I don't really understand the code, I've based it on a code I've found, but I don't understand pointers nor what does:

byte *ptr = (byte *)data_ptr;

@ TheMemberFormerlyKnownAsAWOL:

Just write another function that takes a pointer to the struct and prints the contents.

That's what I actually try to do but I don't understand how to do it^^.
What I don't understand is that the EEPROM.get(eeAddress, customVar) is not the as fram.read8(address), how to I store the incoming bytes in the struct ?

@ wildbill:
If it is fine, why am I not getting the values ?

I should get

1,102,569,3.14,100000

but instead I got

1,102,14594,-2744.00,-24575

What am I doing wrong ?

Just read the data from the FRAM byte-by-byte and store it into the pointer pointing to the struct’s address

void main()
{

	save_data(&data);

	restore_data(&data);

	Serial.print(data.variable0);
	Serial.print(data.variable1);
	Serial.print(data.variable2);
	Serial.print(data.variable3);
	Serial.print(data.variable4);
}


void save_data(struct program_data *data_ptr)
{
	byte *ptr = (byte *)data_ptr;
	
	for (size_t i = 0; i < sizeof(struct program_data); i++)
	{
		fram.write8(i, ptr[i]);
	}
}

void restore_data(struct program_data *data_ptr)
{
	byte *ptr = (byte *)data_ptr;
	
	for (size_t i = 0; i < sizeof(struct program_data); i++)
	{
		ptr[i] = fram.read8(i);
	}
}

Just think of the struct as a single block of contiguous memory. When you dump the data to the FRAM, you copy it byte-by-byte regardless whether it’s a float, uint64_t, byte, etc. When reading it, you do the same but in the opposite direction. byte[0] of FRAM goes to byte[0] of struct’s address, byte[1] goes to the next byte of the structs, and so on.

  data.variable4 = (temporaryArray[7] << 16) | (temporaryArray[8] << 8) | (temporaryArray[9]);
  
}

Oops.

Hi guys thanks for your answers.

It is working now and thanks for your explanation, if think I’m slowly getting my head around it.
As wildbill said I made things overcomplicated.

I still wonder why it didn’t work “my way” because I did basically what hzrnbgy did right ? Instead of ptr *= fram.read8(i); [/color]I did this : *
* *  for (size_t i = 0; i < sizeof(struct program_data); i++) {     //    Serial.print(fram.read8(i)); Serial.print(",");     temporaryArray[i] = fram.read8(i);   }   data.variable0 = temporaryArray[0];   data.variable1 = temporaryArray[1];   data.variable2 = (temporaryArray[2] << 8) | (temporaryArray[3]);   data.variable3 = (temporaryArray[4] << 16) | (temporaryArray[5] << 8) | (temporaryArray[6]);   data.variable4 = (temporaryArray[7] << 16) | (temporaryArray[8] << 8) | (temporaryArray[9]);* *
Is the concatenate wrong ?
*I have one last issue. *
I’ve tried saving multiple struct in a row and then print them all at once.
```
/
  31.12.2020
  Based on : Arduino I2C external 32Kb fram data organization - Arduino Stack Exchange
  https://forum.arduino.cc/index.php?topic=720754.new#new
  https://forum.arduino.cc/index.php?topic=618910.0
*/

#include <Wire.h>
#include “Adafruit_FRAM_I2C.h”

Adafruit_FRAM_I2C fram = Adafruit_FRAM_I2C();
uint16_t framAddr = 0;

struct program_data {
  int variable0;
  long variable1;
  float variable2;
} data;

int nbWrites = 0;
int address = 0;
size_t i = 0;

void setup() {
  Serial.begin(9600);
  fram.begin(0x50);
}

void loop() {
  // Get data
  data.variable0 = nbWrites;
  data.variable1 = 100 + nbWrites;
  data.variable2 = 100 + nbWrites;
  nbWrites++;

// Save data
  save_data(&data);

// Print data
  if (nbWrites > 10) {
    address = 0;
    for (int n = 0; n < nbWrites; n++) {
      restore_data(&data);
    }
    while (true);
  }
}

void save_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

for (size_t i = 0; i < sizeof(struct program_data); i++) {
    fram.write8(address + i, ptr[i]);
  }
  address = address + i;
}

void restore_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

for (size_t i = 0; i < sizeof(struct program_data); i++) {
    ptr[i] = fram.read8(address + i);
  }

address = address + i;
  Serial.print(data.variable0); Serial.print(",");
  Serial.print(data.variable1); Serial.print(",");
  Serial.print(data.variable2);
  Serial.println("");
}*
* *But what I get is this : * *
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00
10,110,110.00

* *It prints 10 times the last saved data instead of each data (I hope it is understandable). That's what is should look like. * *
0,100,100.00
1,101,101.00
2,102,102.00
3,103,103.00
4,104,104.00
5,105,105.00
6,106,106.00
7,107,107.00
8,108,108.00
9,109,109.00
10,110,110.00

```
Why does it do that ?

Having a global i and a local i is quite.confusing.

Is the concatenate wrong ?

You already answered thatvariable3 = (temporaryArray[4] << 16)... the expression on the right is treated as an “int”.
On this platform, an “int” is sixteen bits.
What happens when you shift an “int” sixteen bits to the left?

Also, you apparently have a tacit assumption that float and long each occupy three bytes. Actually, it's four.

Thanks for your help, I’ve been able to get it to work for the int and long variable. By doing this I get the right values when I restore them. I’m still not able to get the float out correctly but I’m working on it.

/*
  31.12.2020
  Based on : https://arduino.stackexchange.com/questions/76951/arduino-i2c-external-32kb-fram-data-organization
*/

#include <Wire.h>
#include "Adafruit_FRAM_I2C.h"

Adafruit_FRAM_I2C fram     = Adafruit_FRAM_I2C();
uint16_t          framAddr = 0;

struct program_data {
  bool variable0;
  byte variable1;
  int variable2;
  long variable3;
  float variable4;
} data;

uint8_t temporaryArray[11];

void setup() {
  Serial.begin(9600);
  fram.begin(0x50);
  Serial.println("");

  data.variable0 = 1;
  data.variable1 = 102;
  data.variable2 = 570;
  data.variable3 = 120000;
  data.variable4 = 43.79;

  save_data(&data);
  restore_data(&data);
}

void loop() {

}

void save_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++) {
    fram.write8(i, ptr[i]);
  }
}

void restore_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (size_t i = 0; i < sizeof(struct program_data); i++) {
    //    Serial.print(fram.read8(i)); Serial.print(",");
    temporaryArray[i] = fram.read8(i);
  }
  data.variable0 = temporaryArray[0];
  data.variable1 = temporaryArray[1];
  data.variable2 = (temporaryArray[3] << 8) | (temporaryArray[2]);
  data.variable3 = ((uint32_t)temporaryArray[7] << 24) | ((uint32_t)temporaryArray[6] << 16) | ((uint32_t)temporaryArray[5] << 8) | (uint32_t)temporaryArray[4];

  Serial.print(data.variable0); Serial.print(",");
  Serial.print(data.variable1); Serial.print(",");
  Serial.print(data.variable2); Serial.print(",");
  Serial.print(data.variable3); Serial.print(",");
  // Serial.print(data.variable4); Serial.print(",");
  Serial.println();
}

Take another look at reply #5 and reply #4

I’ve finally managed to make it work.
Thanks for all you advices and tips.

This code stores data of different types using a struct byte per byte and then prints it all.

/*
  31.12.2020
  Based on :
  https://arduino.stackexchange.com/questions/76951/arduino-i2c-external-32kb-fram-data-organization
  https://forum.arduino.cc/index.php?topic=720754.new#new
  https://forum.arduino.cc/index.php?topic=618910.0
*/

#include <Wire.h>
#include "Adafruit_FRAM_I2C.h"

Adafruit_FRAM_I2C fram = Adafruit_FRAM_I2C();
uint16_t framAddr = 0;

struct program_data {
  bool variable0;
  byte variable1;
  int variable2;
  long variable3;
  float variable4;
} data;

int nbWrites = 0;
int address = 0;
int i = 0;


void setup() {
  Serial.begin(9600);
  fram.begin(0x50);
}

void loop() {
  // Get data
  data.variable0 = 1;
  data.variable1 = nbWrites;
  data.variable2 = 100 + nbWrites;
  data.variable3 = 100 + nbWrites;
  data.variable4 = 100 + nbWrites;
  nbWrites++;

  // Save data
  save_data(&data);

  // Print data
  if (nbWrites > 9) {
    address = 0;
    for (int n = 0; n < nbWrites; n++) {
      restore_data(&data);
    }
    while (true);
  }
}


void save_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (i = 0; i < sizeof(struct program_data); i++) {
    fram.write8(address + i, ptr[i]);
  }
  address = address + i;
}

void restore_data(struct program_data *data_ptr) {
  byte *ptr = (byte *)data_ptr;

  for (i = 0; i < sizeof(struct program_data); i++) {
    ptr[i] = fram.read8(address + i);
  }

  address = address + i;
  Serial.print(data.variable0); Serial.print(",");
  Serial.print(data.variable1); Serial.print(",");
  Serial.print(data.variable2); Serial.print(",");
  Serial.print(data.variable3); Serial.print(",");
  Serial.print(data.variable4);
  Serial.println("");
}

Why not one function to write, one function to read and one function to print?

(The functions to read and write should also accept an address. They should not themselves modify this address)