Storing and retreving an array from EEPROM

Hi,

I’m just starting trying to get my head around EEPROM, I’m pretty sure I understand the Get() and put() for a single Byte and have some code to read an Int with 2 bytes and assemble them and the same for a long with 4 bytes.

I’ve been reading various posts regarding EEPROM and arrays but a lot are well over 5 years and some posts allude to things changing since then with new libraries available.

example of the code I have to read from EEPROM, I have only just learn what bit shifting is so if there is an easier way to retrieve ints and longs please let me know.

//                            E E P R O M R e a d I n t ( )
//********************************************************************************
int EEPROMReadInt(int address)
{
  //Read the 2 bytes from the eeprom memory.
  long one  = EEPROM.read(address);      //LSB
  long two  = EEPROM.read(address + 1);  //MSB

  int r = 0;

  //assemble the bytes into an 'int'
  r = r + (two << 8);
  r = r +  one;

  return r;

} //END of EEPROMReadInt()

What I am struggling with is how an array is stored in EEPROM. I also have seen arrays written differently than what I am using.

The things I want to store are (the times are just for testing) this is part of my overall spa pool controller

//*******************************************************************************
// structure to store comfort times
typedef struct {
  byte On_hrs;
  byte On_min;
  byte Off_hrs;
  byte Off_min;
  byte Freq;  // Weekdays, Weekends, Everyday
} Comfort;

// 0 = off
// 1 = Weekends
// 2 = weekdays
// 3 = everyday

// array with comfort times; you probably want to move this to EEPROM so it will be remembered between power cycles.
Comfort comfortTimes[] =
{
  {16, 00, 16, 05, 3},  // Comfort 0 On Everyday
  {16, 10, 16, 15, 1},  // Comfort 1 On Weekends only
  {16, 57, 16, 59, 2},  // Comfort 2 Weekdays Only
  {19, 00, 20, 00, 1},  // Comfort 1 everyday
  {5, 30, 6, 30, 0},    // Comfort 4 not set
  {7, 00, 9, 30, 0},    // Comfort 5  not set
}; // array of 6 items


//*********************************************************************************
// Variables for Control Settings
byte maxComfTemp            = 45;   // Set the max temperature the spa can heat to
byte minComfTemp            = 15;   // Set the minimum for comfort while spa in use
byte comfTemp               = 24;   // The target comfort temp of spa
byte maxEcoTemp             = 23;   // The max temp during eco mode
byte minEcoTemp             = 25;   // the min temp during eco mode

unsigned long minLightLevel = 400;  // light level to turn lights on during exit delay

Most are bytes with only 1 long to work out but the array is giving me trouble.

Firstly should I be writing my array as

comfortTimes[6] [5] = as it is 6 rows and 5 columns or because it is of type “Comfort” which is a structure of 5 items is the array just six items of type “comfort”?

Then storing and retrieving, is this the correct way to approach it (for someone just starting)
comfortTimes[0] going to save into address 0 to 4 and
ComfortTimes[1] into 5 to 9
ComfprtTimes[2] into 10 14
etc
Assuming I start saving at 0

Then I have to work out a function to update the correct set of address when the times are edited
currently any edit happens on the full row rather than an X:Y of the array itself but it is a matter of then reassembling the array from the address at startup

comfortTimes[2].On_hrs = address 10
comfortTimes[2].On_min = address 11
etc to address 14

I should also say that changes to any settings would be infrequent and using the EEPROM is just cope with our numerous power cuts. I’ll probably try and use the update() as not many values will change.

Does anyone know of any more recent posts/examples or other information I can read/look up.

Cheers
Al

PS my full current code is attached

spa_controller_V4.ino (62.3 KB)

One of the issues you might need to look at is the fact that structs can have different sizes in memory on different architectures and compilers. To ensure the code is a fast as possible the compiler can use padding (inserting empty bytes) to align members of a struct in memory for faster access.

So, when you copy your array of structs into EEPROM your addresses might not be correct.

You can tell the compiler to pack structs to avoid padding. This depends on the Arduino you are using and looks something like struct attribute( ( packed ) ) ... .

For an array you define the type of the array and the number of elements and the compiler figures out how much memory you need in total. So, for RAM you do not need to worry, just when you copy to EEPROM.

this is what I used:

#include <Wire.h>    //use SCL & SDA EEPROM
//extern TwoWire Wire1;//use SCL1 & SDA1 EEPROM
#define disk1 0x50    //Address of 24LC256 eeprom chip
/* use Wire1.xxx for SCL1 SDA1 */
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte Number) {
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(Number);
  Wire.endTransmission();
  delay(6);
  Number = 0;
  address = 0;
}

byte readEEPROM(int deviceaddress, unsigned int eeaddress ) {
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress, 1);
  if (Wire.available()) rdata = Wire.read();
  return rdata;
}

can do 6 characters or more (I only do 6)

Thanks Klaus

I'll read up on the struct attribute( ( packed ) ) seems like it is a bit more advanced than the standard aurduino reference library. :slight_smile:

So I'll leave definition of the array alone and the "packed' will allow me to use the bytes next to each other 0-4, 5-9, 10-4 etc for each array element.

fnb111 - I'm guessing that disk1 0x50 is an external EEPROM rather than the onboard chip. I'll have to study that a bit more.

Cheers
Al

Ok, so after quite a bit of reading which lead into the world of pointers and trying to create an array of pointer objects back to the variables and then save the array (don’t think it was going to work)

I think I came up with a simple solution. Storing the values I want to save into typdef struct and then saving the array with EEPROM.put() and EEPROM.get() rather than saving each setting and recompiling the unsigned long variables

My spa on and off times was already an array so it was just a matter of converting the settings I want saved to a typedef struct.

I have tested the saving but not the retrieving, any comments, is this the best (simplest) approach, I don’t need to save and restore specific values other than to survive power cuts.

 #include <EEPROM.h>

// structure to store comfort times
typedef struct {
  byte On_hrs;
  byte On_min;
  byte Off_hrs;
  byte Off_min;
  byte Freq;  // Weekdays, Weekends, Everyday
}__attribute__((packed)) Comfort;

Comfort comfortTimes[] =
{
  {16, 00, 16, 05, 1},  // Comfort 0 On Everyday
  {16, 10, 16, 15, 1},  // Comfort 1 On Weekends only
  {16, 57, 16, 59, 3},  // Comfort 2 Weekdays Only
  {19, 00, 20, 00, 2},  // Comfort 1 everyday
  {5, 30, 6, 30, 0},     // Comfort 4 not set
  {7, 00, 9, 30, 0},     // Comfort 5  not set
}; // array of 6 items


//*********************************************************************************
// Variables for Control Settings

typedef struct {
        byte maxComfTemp;      //Set the max temperature the spa can heat to
        byte minComfTemp;      //Set the minimum for comfort while spa in use
        byte maxEcoTemp;       //The max temp during eco mode
        byte minEcoTemp;      //the min temp during eco mode

        unsigned long minLightLevel; //light level to turn lights on during exit delay
        unsigned long ExitTimeDelay; // set at 30 sec for testing
        unsigned long InUseTime;     // time before auto exit delay
}__attribute__((packed))SETUP;

SETUP Settings[] =
{45, 15, 23, 25, 400, 30*1000ul, 30*1000ul};

byte SettingsAddr = (sizeof(comfortTimes)+1); // adds a one buffer space

void SaveComfortTimeEEPROM()   // function to be called after times changed
EEPROM.put(0, comfortTimes);  // starting at address 0
}

void SaveSettingsEEPROM()     // function to call after settings changed
{
EEPROM.put(SettingsAddr, Settings);  //starting at address 31
}

void RetrieveEEPROM() // Would this go in setup or in the Startup state of the state machine
{
  EEPROM.get(0, comfortTimes);
  EEPROM.get(SettingsAddr, Settings);
}

void setup()
{}

void loop()
{}

EEPROM.put and EEPROM.get are the way to go. In your code I would change the type of settingsAddr to an int (or unsigned int). There is a chance that you decide to add more comfort times in the future and it will exceed 256 bytes.

Have you look at this ?

Reading and Writing Data Structures to EEPROM
https://playground.arduino.cc/Code/EEPROMWriteAnything/

No I hadn't seen that, I'll have a read. I have been playing around with the examples off the EEPROM library. (which one is broken and has no code)

sterretje:
EEPROM.put and EEPROM.get are the way to go. In your code I would change the type of settingsAddr to an int (or unsigned int). There is a chance that you decide to add more comfort times in the future and it will exceed 256 bytes.

Thanks will change now.

Cheers
Al

larryd:
Have you look at this ?

Reading and Writing Data Structures to EEPROM
Arduino Playground - EEPROMWriteAnything

So reading through that and the linked post topic https://forum.arduino.cc/index.php?topic=239629.0
the .pu() and .get() were added somewhere between 2014 and 2018. and the .put() uses the the .update() method to save write cycles.

I did notice on the reference site to .update() it didn't say it worked with custom struct.

Cheers
Al

larryd:
Have you look at this ?

Reading and Writing Data Structures to EEPROM
Arduino Playground - EEPROMWriteAnything

The use of EEPROMWriteAnything is no longer needed as EEPROM.put and EEPROM.get achieve the same.

Everybody their preference though :wink: