Odd EEPROM issue on EP8266-01

I have need to take values from a web served form and commit them to EEPROM for later use. As I am making a heater timer/controller and want to store setpoints and switch times using web server, via sinric, or some control manually. The web form is handled correctly, stores the variables in RAM OK as it then reads them back into the served webpage. As last part of that I call a write value sub routine to pass all the values into flash. So this works for 28 bytes of time value, string name, and various other parameters, but one of the setpoints refuses to store! I’ve called out begin, commit, etc, and added a delay post commit and read as suggested elsewhere, but whilst everything else stores and reads back, this one value (Set1) doesn’t, any ideas? I’ve tried different registers, clearing the flash, changing the name, but this byte just will not store correctly, yet variable reads fine elsewhere. (The setpoint is stored as 255 steps between 10 and 35°C and operated on elsewhere).

Read Routine:

void readValues() {
  for (int k = 0; k <= 6; k++) {
    EEPROM.get(k, onHour[k] );              //0 - 6 (EEPROM Address) Sunday to Saturday
    EEPROM.get(k + 7, onMin[k] );         //7 - 13
    EEPROM.get(k + 15, offHour[k] );        //14 - 20
    EEPROM.get(k + 22, offMin[k] );        //21 - 28
  }
  byte j;
  MyHostName = "";
  EEPROM.get(40, j);        //length of string

  for (int l = 0; l < j; l++) {  //get the string as individual characters and concatanate
    char k;
    EEPROM.get(41 + l, k);
    MyHostName = MyHostName + k;
  }

  EEPROM.get(30, Set1);
  EEPROM.get(31, Set2);
  EEPROM.get(32, Set3);

  EEPROM.get(60, Kp);
  EEPROM.get(61, Ki);
  EEPROM.get(62, Kd);

  delay(500);
}

Write Routine:

bool writeValues() {
  for (int i = 0; i <= 6; i++) {
    EEPROM.put(i, onHour[i]);              //0 - 6 (EEPROM Address) Sunday to Saturday
    EEPROM.put(i + 7, onMin[i]);           //7 - 13
    EEPROM.put(i + 15, offHour[i]);        //14 - 20
    EEPROM.put(i + 22, offMin[i]);         //21 - 28
  }

  EEPROM.put(30, Set1 );
  EEPROM.put(31, Set2 );
  EEPROM.put(32, Set3 );

  byte varLen = (MyHostName).length();
  EEPROM.put(40, varLen);                   //put the variable length to EEPROM

  char myName[16];
  strcpy(myName, MyHostName.c_str());
  EEPROM.put(41, myName);                  //put the Name to EEPROM

  for (int i = 0 + varLen; i <= 16; i++) { //add ASCII blanks to remaining registers
    EEPROM.put(i + 41 + varLen, 0x20);
  }

  EEPROM.put(60, Kp);
  EEPROM.put(61, Ki);
  EEPROM.put(62, Kd);

  bool error = EEPROM.commit();
  delay(200);
#ifdef serialActive
  if (error) {
    Serial.println("ERROR! EEPROM commit failed");
  } else {
    Serial.println("EEPROM successfully committed");
  }
#endif
  return error;
}

I suggest that you put your data in a struct. Then you can save it with a single put() and load it with a single get()

Posting snippets of code is not useful. For instance, what data type is Set1 ? If it is anything other than a byte then the save/load will not work

Have you tried printing Set1 before you save it ?
Is its value what you expect ?

a struct will avoid these hardcoded numbers of “EEPROM addresses”.

Here is a short example of a how to keep several variables in a struct, and how to read or write these values:

#include <EEPROM.h>

constexpr size_t wifidatasize = 42;

// some initial values:
char ssid[wifidatasize]     = "mySSID";
char password[wifidatasize] = "myPassword";
byte wlan = 42;

// EEPROM uses a struct for all adresses
// and enables access to the values with offsetof
struct Addr
{
  byte aByte;                 
  char ssid[wifidatasize];             
  char password[wifidatasize];        
};

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println(F("\nEEPROM structure"));
  EEPROM.begin(4096);

  Serial.println(F("as compiled"));
  debugValue();
  // how to read
  eepromReadAll();
  Serial.println(F("after read"));
  debugValue();
  
  // how to write
  //eepromSaveAll();
}

void loop() {
}

void eepromReadAll()
{
  //EEPROM.get(offsetof(struct, address), yourDestinationVariable);
  EEPROM.get(offsetof(Addr, aByte), wlan);   // get the value of address "aByte" and copy it to variable wlan
  EEPROM.get(offsetof(Addr, ssid), ssid);
  EEPROM.get(offsetof(Addr, password), password);
}

void eepromSaveAll()
{
  //EEPROM.put(offsetof(struct, address), yourSourceValue);
  EEPROM.put(offsetof(Addr, aByte), 4);
  EEPROM.put(offsetof(Addr, ssid), "newSSID");
  EEPROM.put(offsetof(Addr, password), "newPassword");
  EEPROM.commit();
}

void debugValue()
{
  Serial.print(F("wlan    =")); Serial.println(wlan);
  Serial.print(F("ssid    =[")); Serial.print(ssid); Serial.println("]");
  Serial.print(F("password=[")); Serial.print(password); Serial.println("]");
}

Unfortunately didn't get time yeterday to work with this. However I do have a question around the placing of variables within the 'struct', how do I then access them elsewhere, can I still call them as before? In the example shown could I still call ssid? Or is it something like addr.ssid?

Or is it best to put the values into the struct in the sub routine so I don't have to change use elsewhere? As its going to be a very infrequent use (how often do you change a timeswitch!)

BTW, I did mention that Set1 was a byte and reads back OK from RAM in original description, perhaps not as clearly as I might.

You access variables in a struct using structName.variableName and use them just like any other variable anywhere they are in scope

Unfortunately didn't get time yeterday to work with this. However I do have a question around the placing of variables within the 'struct', how do I then access them elsewhere, can I still call them as before? In the example shown could I still call ssid? Or is it something like addr.ssid?

In my example the struct definition only serves for the management of the "EEPROM addresses". There is no instance of the struct Addr. There is currently no instance called addr.

You can access each eeprom variable in your code with this pattern:

//EEPROM.get(offsetof(struct, address), yourDestinationVariable);

for example:
the value stored add address from aByte will be copied into your variable wlan:

EEPROM.get(offsetof(Addr, aByte), wlan);

Presumably you are going to need the values of all of the elements of the struct at some point in the program otherwise why store them ?

It would be simpler from the coding point of view if you simply got the whole struct back from EEPROM then used the variables it contained as normal. Certainly easier to implement and understand.

it depends on the usecase.
Some values are needed once in the setup only, some are needed during runtime. Some are readed more often but I still don't waste a global.

OK, thanks for this, had a test with the struct and got it to read & write some values as sketch below.

Is there a quick way to get the time arrays into the struct? onHour etc.?

And why when I post code does it split it all up?

#include <EEPROM.h>
byte Kp = 2;
byte Ki = 5;
byte Kd = 1;  //byte values
byte Set1 = 120;   //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), occupied on
byte Set2 = 80;   //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), occupied off
byte Set3 = 60;  //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), unoccupied
byte onHour[7] = {0,8,8,8,8,8,0};           //Switch Time Array, Sunday to Saturday
byte onMin[7] =  {0,0,0,0,0,0,0};
byte offHour[7] ={0,17,17,17,17,17,0};
byte offMin[7]=  {0,0,0,0,0,0,0};
char MyHostName[17] = "Office_Rad";      //WiFi Hostname and switch name
struct Addr
{
  byte prop;
  byte integral;  
  byte diff;
  byte set1;
  byte set2;
  byte set3;  
  char hostName[17];        
};
void setup() {
  // put your setup code here, to run once:
  EEPROM.begin(512);
  Serial.begin(115200);
  Serial.println(F("\nEEPROM structure"));
  Serial.println(F("as compiled"));
  debugValue();
  // how to read
  eepromReadAll();
  Serial.println(F("after read"));
  debugValue();
  
  // how to write
  eepromSaveAll();
    Serial.println(F("after read"));
}
void loop() {
  // put your main code here, to run repeatedly:
delay(5000);
debugValue();
}
void eepromReadAll()
{
  //EEPROM.get(offsetof(struct, address), yourDestinationVariable);
  EEPROM.get(offsetof(Addr, prop), Kp);
  EEPROM.get(offsetof(Addr, integral), Ki);
  EEPROM.get(offsetof(Addr, diff), Kd);
  EEPROM.get(offsetof(Addr, set1), Set1);
  EEPROM.get(offsetof(Addr, set2), Set2);
  EEPROM.get(offsetof(Addr, set3), Set3);
  EEPROM.get(offsetof(Addr, hostName), MyHostName);
}
void eepromSaveAll()
{
  //EEPROM.put(offsetof(struct, address), yourSourceValue);
  EEPROM.put(offsetof(Addr, prop), 4);
  EEPROM.put(offsetof(Addr, integral), 10);
  EEPROM.put(offsetof(Addr, diff), 2);
  EEPROM.put(offsetof(Addr, set1), 130);
  EEPROM.put(offsetof(Addr, set2), 95);
  EEPROM.put(offsetof(Addr, set3), 55);
  EEPROM.put(offsetof(Addr, hostName), "OfficeRadiator");
  EEPROM.commit();
}
void debugValue()
{
  Serial.print(F("Kp    =")); Serial.println(Kp);
  Serial.print(F("Ki    =")); Serial.println(Ki);
  Serial.print(F("Kd    =")); Serial.println(Kd);
  Serial.print(F("Set1  =")); Serial.println(Set1);
  Serial.print(F("Set2  =")); Serial.println(Set2);
  Serial.print(F("Set3  =")); Serial.println(Set3);
  Serial.print(F("HostName=\"")); Serial.print(MyHostName); Serial.println("\"");
}

Is there a quick way to get the time arrays into the struct? onHour etc.?

It is not clear what you mean. You need to enter the values into the array somewhere

And why when I post code does it split it all up?

If you did not explicitly use [­code] [­/code] several times then please explain how you inserted the code into your post if that is what you mean

define a struct for your time information.

struct Timer {
  byte onHour;           //Switch Time Array, Sunday to Saturday
  byte onMin;
  byte offHour;
  byte offMin;
}

add an array with 7 elements like any other variable to your struct.

Timer timer[7];

On code, I pressed ‘insert code’ (</>) and pasted. Earleir I went back and unsplit it, but it did it again.

I’ll try the other struct, thanks.

Have to say I’m struggling with structures, get it that its basically an array with different data types in it, but I’m struggling with getting multiple values into it. Variables are defined as code above.

So I defined the structure:

struct TS {
  byte onH;           //Switch Time Array, Sunday to Saturday
  byte onM;
  byte offH;
  byte offM;
};

within the writeto EEPROM sub-routine I tried various combinations like:

for (byte i = 0; i <=6 ; i++) {
  EEPROM.put(offsetof(TS, onH), onHour[i]);
  EEPROM.put(offsetof(TS, onM), onMin[i]);
  EEPROM.put(offsetof(TS, offH), offHour[i]);
  EEPROM.put(offsetof(TS, offM), offMin[i]);
}

tried ‘TS i’ but it doesn’t appear to accept a variable as a name, and tried writing the whole byte array as well.

get statement is:

  EEPROM.get(offsetof(TS, onH), onHour);
  EEPROM.get(offsetof(TS, onM), onMin);
  EEPROM.get(offsetof(TS, offH), offHour );
  EEPROM.get(offsetof(TS, offM), offMin);

but its all returning nonsense before it even gets to the get!

TS is your struct. you need an instance of TS also!

then, you use your instance.

show a full sketch what we can put in the IDE!

EEPROM.get(offsetof(TS, onH), onHour);
EEPROM.get(offsetof(TS, onM), onMin);
EEPROM.get(offsetof(TS, offH), offHour );
EEPROM.get(offsetof(TS, offM), offMin);

??

Why not simply

  EEPROM.put(address, structName);
//then later
  EEPROM.get(address, structName);

That will save/load the whole struct in one command and you can then access the struct variables as normal

Whole Code;
[­code]
#include <EEPROM.h>

byte Kp = 2;
byte Ki = 5;
byte Kd = 1; //byte values
byte Set1 = 120; //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), occupied on
byte Set2 = 80; //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), occupied off
byte Set3 = 60; //setpoint value between 10 and 35.5°C in 0.1 steps (/10 and add 10 to get value), unoccupied
byte onHour[7] = {0, 8, 8, 8, 8, 8, 0}; //Switch Time Array, Sunday to Saturday
byte onMin[7] = {0, 0, 0, 0, 0, 0, 0};
byte offHour[7] = {0, 17, 17, 17, 17, 17, 0};
byte offMin[7] = {0, 0, 0, 0, 0, 0, 0};
char MyHostName[17] = “Office_Rad”; //WiFi Hostname and switch name

struct Addr
{
byte prop;
byte integral;
byte diff;
byte set1;
byte set2;
byte set3;
char hostName[17];
};

struct TS {
byte onH; //Switch Time Array, Sunday to Saturday
byte onM;
byte offH;
byte offM;
};

void setup() {
// put your setup code here, to run once:
EEPROM.begin(512);
Serial.begin(115200);
Serial.println(F("\nEEPROM structure"));

Serial.println(F(“as compiled”));
debugValue();
// how to read
eepromReadAll();
Serial.println(F(“after read”));
debugValue();

// how to write
eepromSaveAll();
Serial.println(F(“after read”));
}

void loop() {
// put your main code here, to run repeatedly:
delay(5000);
debugValue();
}

void eepromReadAll()
{
//EEPROM.get(offsetof(struct, address), yourDestinationVariable);
EEPROM.get(offsetof(Addr, prop), Kp);
EEPROM.get(offsetof(Addr, integral), Ki);
EEPROM.get(offsetof(Addr, diff), Kd);
EEPROM.get(offsetof(Addr, set1), Set1);
EEPROM.get(offsetof(Addr, set2), Set2);
EEPROM.get(offsetof(Addr, set3), Set3);
EEPROM.get(offsetof(Addr, hostName), MyHostName);

EEPROM.get(offsetof(TS, onH), onHour);
EEPROM.get(offsetof(TS, onM), onMin);
EEPROM.get(offsetof(TS, offH), offHour );
EEPROM.get(offsetof(TS, offM), offMin);

}

void eepromSaveAll()
{
//EEPROM.put(offsetof(struct, address), yourSourceValue);
EEPROM.put(offsetof(Addr, prop), 4);
EEPROM.put(offsetof(Addr, integral), 10);
EEPROM.put(offsetof(Addr, diff), 2);
EEPROM.put(offsetof(Addr, set1), 130);
EEPROM.put(offsetof(Addr, set2), 95);
EEPROM.put(offsetof(Addr, set3), 55);
EEPROM.put(offsetof(Addr, hostName), “OfficeRadiator”);

for (byte i = 0; i <=6 ; i++) {
EEPROM.put(offsetof(TS, onH), onHour*);*
_ EEPROM.put(offsetof(TS, onM), onMin*);_
_ EEPROM.put(offsetof(TS, offH), offHour);
EEPROM.put(offsetof(TS, offM), offMin);
}
EEPROM.commit();
}
void debugValue()
{
Serial.print(F(“Kp =”)); Serial.println(Kp);
Serial.print(F(“Ki =”)); Serial.println(Ki);
Serial.print(F(“Kd =”)); Serial.println(Kd);
Serial.print(F(“Set1 =”)); Serial.println(Set1);
Serial.print(F(“Set2 =”)); Serial.println(Set2);
Serial.print(F(“Set3 =”)); Serial.println(Set3);
Serial.print(F(“HostName=”")); Serial.print(MyHostName); Serial.println(""");
for (int i = 0; 0 <= 6; i++) {
Serial.print(F("OnTime ")); Serial.print(onHour); Serial.print(":"); Serial.print(onMin);
Serial.print(F("\tOffTime ")); Serial.print(offHour); Serial.print(":"); Serial.println(offMin);
}
}
[­/code]*_

when you want to keep your variables anyway in global scope, you can really just define your struct and put/commit it to EEPROM emulation.

#include <EEPROM.h>

const size_t noOfTimes = 7;

struct TS {
    byte onH;           //Switch Time Array, Sunday to Saturday
    byte onM;
    byte offH;
    byte offM;
};

struct Data
{
  byte kp; //prop;      // Kp
  byte ki; //integral;  // Ki
  byte kd; //diff;      // Kd
  byte set1;
  byte set2;
  byte set3;
  char hostName[17];
  TS ts[noOfTimes];
};

// one instance including initial values
Data data {
  2,
  5,
  1,
  120,
  80,
  60,
  "Office_Rad",
  {
    {0, 0, 0, 0},
    {8, 0, 17, 0},
    {8, 0, 17, 0},
    {8, 0, 17, 0},
    {8, 0, 17, 0},
    {8, 0, 17, 0},
    {0, 0, 0, 0}
  }
};

void setup() {
  // put your setup code here, to run once:
  EEPROM.begin(512);
  Serial.begin(115200);
  Serial.println(F("\nEEPROM structure"));
  Serial.println(F("1. as compiled"));
  debugValue();
  // how to read
  eepromReadAll();
  Serial.println(F("\n2. after read"));
  debugValue();

  // how to write
  eepromSaveAll();                  // comment after first upload to see the difference
  Serial.println(F("\n3. after write")); // comment after first upload to see the difference
  debugValue();                     // comment after first upload to see the difference
}

void loop() {
  // put your main code here, to run repeatedly:

}

void eepromReadAll()
{
  EEPROM.get(0, data);
}

void eepromSaveAll()
{
  //change some values
  data.kp = 4;
  data.ki = 10;
  data.kd = 2;
  data.set1 = 130;
  data.set2 = 95;
  data.set3 = 55;
  strcpy(data.hostName, "OfficeRadiator");
  // just some test values:
  data.ts[0].onH = 10;
  data.ts[0].onM = 10;
  data.ts[0].offH = 10;
  data.ts[0].offM = 10;
  data.ts[1].onH = 11;
  data.ts[1].onM = 11;
  data.ts[1].offH = 11;
  data.ts[1].offM = 11;
  data.ts[2].onH = 12;
  data.ts[2].onM = 12;
  data.ts[2].offH = 12;
  data.ts[2].offM = 12;
  data.ts[3].onH = 13;
  data.ts[3].onM = 13;
  data.ts[3].offH = 13;
  data.ts[3].offM = 13;
  data.ts[4].onH = 14;
  data.ts[4].onM = 14;
  data.ts[4].offH = 14;
  data.ts[4].offM = 14;
  data.ts[5].onH = 15;
  data.ts[5].onM = 15;
  data.ts[5].offH = 15;
  data.ts[5].offM = 15;
  data.ts[6].onH = 16;
  data.ts[6].onM = 16;
  data.ts[6].offH = 16;
  data.ts[6].offM = 16;
  // persist data
  EEPROM.put(0, data);
  EEPROM.commit();
}

void debugValue()
{
  Serial.print(F("Kp    =")); Serial.println(data.kp);
  Serial.print(F("Ki    =")); Serial.println(data.ki);
  Serial.print(F("Kd    =")); Serial.println(data.kd);
  Serial.print(F("Set1  =")); Serial.println(data.set1);
  Serial.print(F("Set2  =")); Serial.println(data.set2);
  Serial.print(F("Set3  =")); Serial.println(data.set3);
  Serial.print(F("HostName=\"")); Serial.print(data.hostName); Serial.println("\"");
  for (uint8_t i = 0; i < noOfTimes; i++) {
    Serial.print(F("OnTime ")); Serial.print(data.ts[i].onH); Serial.print(":"); Serial.print(data.ts[i].onM);
    Serial.print(F("\tOffTime ")); Serial.print(data.ts[i].offH); Serial.print(":"); Serial.println(data.ts[i].offM);
  }
}

When you upload the sketch the first time you will see

  • the “default” values from the program
  • some “random” values from your EEPROM
  • the new Values.

Then, comment the last three lines from setup and run the sketch again:
now you should get:

  • the “default” values from the program
  • the values from your EEPROM, you have written just before.

Just remember: ALL data is keept as globals in this Version.

20:15:32.543 -> EEPROM structure
20:15:32.543 -> 1. as compiled
20:15:32.543 -> Kp    =2
20:15:32.543 -> Ki    =5
20:15:32.543 -> Kd    =1
20:15:32.543 -> Set1  =120
20:15:32.543 -> Set2  =80
20:15:32.543 -> Set3  =60
20:15:32.543 -> HostName="Office_Rad"
20:15:32.543 -> OnTime 0:0	OffTime 0:0
20:15:32.543 -> OnTime 8:0	OffTime 17:0
20:15:32.583 -> OnTime 8:0	OffTime 17:0
20:15:32.583 -> OnTime 8:0	OffTime 17:0
20:15:32.583 -> OnTime 8:0	OffTime 17:0
20:15:32.583 -> OnTime 8:0	OffTime 17:0
20:15:32.583 -> OnTime 0:0	OffTime 0:0
20:15:32.583 -> 
20:15:32.583 -> 2. after read
20:15:32.583 -> Kp    =4
20:15:32.583 -> Ki    =10
20:15:32.583 -> Kd    =2
20:15:32.583 -> Set1  =130
20:15:32.583 -> Set2  =95
20:15:32.583 -> Set3  =55
20:15:32.583 -> HostName="OfficeRadiator"
20:15:32.583 -> OnTime 10:10	OffTime 10:10
20:15:32.583 -> OnTime 11:11	OffTime 11:11
20:15:32.583 -> OnTime 12:12	OffTime 12:12
20:15:32.583 -> OnTime 13:13	OffTime 13:13
20:15:32.583 -> OnTime 14:14	OffTime 14:14
20:15:32.583 -> OnTime 15:15	OffTime 15:15
20:15:32.583 -> OnTime 16:16	OffTime 16:16
20:15:32.583 -> 
20:15:32.583 -> 3. after write
20:15:32.583 -> Kp    =4
20:15:32.583 -> Ki    =10
20:15:32.583 -> Kd    =2
20:15:32.583 -> Set1  =130
20:15:32.583 -> Set2  =95
20:15:32.583 -> Set3  =55
20:15:32.583 -> HostName="OfficeRadiator"
20:15:32.583 -> OnTime 10:10	OffTime 10:10
20:15:32.653 -> OnTime 11:11	OffTime 11:11
20:15:32.653 -> OnTime 12:12	OffTime 12:12
20:15:32.653 -> OnTime 13:13	OffTime 13:13
20:15:32.653 -> OnTime 14:14	OffTime 14:14
20:15:32.653 -> OnTime 15:15	OffTime 15:15
20:15:32.653 -> OnTime 16:16	OffTime 16:16

Absolutely brilliant, thank you so much. Keeping variables as globals is not a problem as they need be regularly passed to another processor, and/or acted upon locally. The ESP does the time and web server function, the UNO runs the control of the heater itself with full PID control loop. and display.

Now all all I have to do is understand how I tranfer this into my full sketch and use the variables. Thanks again for all your help.