EEPROM Storage issue

EEPROM.put() automatically uses update() behind the scenes

If you insist on using individual variables then you could do this

#include <EEPROM.h>

int baseAddress = 0;
int i = 26;
int j = 36;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  EEPROM.update(baseAddress, i);
  EEPROM.update(baseAddress + sizeof(i), j);
  EEPROM.get(baseAddress, i);
  EEPROM.get(baseAddress + sizeof(i), j);
  Serial.println(i);
  Serial.println(j);
}

void loop()
{
}

But then you have the task of maintaining it manually should anything change such as the number or type of variables

upto this, is this OK?

struct structLayout {
  
  char devSL[deviceSLlength]; //10 digit device SL No including NULL char [0-9 total 10 + 1]
  boolean optHWEn = false;
  boolean debugEn = false;
  int targetTemp;
  unsigned int deltaT = 4; //default Delta T = 4
  unsigned int opsMode = 1; //default ops mode = 1
  unsigned int compressorRunTime = 12; //default 12 hrs.
  unsigned int defrostTime = 30; //default 30 mins
  char * userPass;
  
};

// The necessary variables for regular operation
boolean startup = true;
boolean systemStateLEDState = LOW;
unsigned long compressorRunCounter = 0;
int indoorTemp;
int finsTemp;
int outdoorTemp;
int lowTempLimit;
int hiTempLimit;
unsigned int errorCode = 0;
boolean systemError = false; //default is no error.
boolean compressor_state = false;
boolean indoor_fan_state = false;
boolean defrost_state = false;
boolean isConfigMode = false;

//necessary variables for running config (via structure)
structLayout runningConf
runningConf.userPass = "1111"; //default user pass = 1111

void setup(void) {
  //whatever goes there
}

void loop() {
  //whatever goes there
}

UKHeliBob:
EEPROM.put() automatically uses update() behind the scenes

If you insist on using individual variables then you could do this

But then you have the task of maintaining it manually should anything change such as the number or type of variables

Normally type of variable will not change once fixed. Value will be changed...
That's what i was actually doing, via #define and in #define, i cant use sizeof right? Thus i was going the method said earlier,

//EEPROPM Addressings need to be re-check for more optimization
#define eepromOffset 0 //[Address 0, reserved]
#define deviceSLAddr (eepromOffset + 1) //[Start at 1, used for device SL (10byte incl NULL), ends at 11]
#define optHWEn_eepromAddr (deviceSLAddr + 11) //[Start at 1+11=12, used for 1byte, ends at 12]
#define debugEn_eepromAddr (optHWEn_eepromAddr + 1) //[Start at 12+1=13, used for 1byte, ends at 13]
#define tempSet_eepromAddr (debugEn_eepromAddr + 1) //[Start at 13+1=14, used for 2byte, ends at 15]
#define deltaT_eepromAddr (tempSet_eepromAddr + 2) //[Start at 15+2=17, used for 1byte, ends at 17]
#define opsMode_eepromAddr (deltaT_eepromAddr + 1) //[Start at 16+1=17, used for 1byte, ends at 17]
#define compressorRunTime_eepromAddr (opsMode_eepromAddr + 1) //[Start at 17+1=18, used for 1byte, ends at 18] //we may avoid it and can put this in a variable as per update.
#define defrostTime_eepromAddr (compressorRunTime_eepromAddr + 1) //[Start at 18+1=19, used for 1byte, ends at 19]
#define userPassword_eepromAddr (defrostTime_eepromAddr + 1) //[Start at 19+1=20, used for 4byte, ends at 23]

upto this, is this OK?

The idea looks OK but it won't compile

  runningConf.userPass = "1111"; //default user pass = 1111

needs to go in setup

deviceSLlength needs to be declared as an integer constant so that it can be used as an array bounds value

i cant use sizeof right?

Why do you say that ?
My example program used sizeof()

aq_mishu:
the structure method is a nice one i do fully agree, but just for the sake of debate here:

I will store several runtime parameters and i may change anyone of those, say debugEnable and then it needs to UPDATE only that section, instead of the whole thing. That was actually my motivation for individual addressing..

I will add another code block soon to let you know what i'm doing

for the sake of debate... If you are playing with eeprom on your dev board, you might have several more writes than in actual use, but even then, it's very unlikely you write to the point of corruption. Also, for the sake of debate, if you read the source code for the eeprom library, you will see that EEPROM.put() uses update, which only writes bytes that have changed, on every single byte in the data structure, so whether it's a char, an int, a long or a large data structure, each byte is only written if it has changed. With individual addressing it's more likely that you will muck up and write over something or not read the right thing... than the likelihood you will exceed the number of writes. You can have the first element in your struct be a version number or key, which you can check against the same value declared as a constant in your program and write some defaults if the check fails. It can be any element, doesn't have to be the first... but this allows you to force an update to the eeprom and data structure by changing the constant value before you compile.

[quote author=Coding Badly date=1602736195 link=msg=4765260]
...except when it doesn't.[/quote]
when does a char not hold a value between -128 and 127? do you mean I should have clarified signed char vs unsigned char (0-255) or am I missing something else?

UKHeliBob:
Or do it the easy way and use EEPROM.get() and EEPROMN.put() to load/save the whole struct at the same time with a single command

Were these always there? I would definitely put these in whatever tutorial I may write eventually. Thank you.

Were these always there?

No, but they have been there for several years now

See https://www.arduino.cc/en/Reference/EEPROM if you have not already done so

on next post, I will start from GRAND START with a bit of breakdown...

Perehama:
when does a char not hold a value between -128 and 127? do you mean I should have clarified signed char vs unsigned char (0-255) or am I missing something else?

char should always be treated as neither because the decision lies in the hands of compiler developer.

signed char or int8_t are the portable (correct) choice.

You would think char should always be unsigned but not always I guess. for most loops char would be fine, doubtless more efficient, and because you think of it ranging from 0 to 255 it should be unsigned.

Well, giving a fresh start from here as said...

The defines are:

// System specific constants
#define baud 9600
#define rxBufSize 30
#define txBufSize 70
#define deviceSLlength 10 //10 digit including NULL char [0-9 total 10 + 1]
#define userPasswordLength 5 //4 char incl NULL (dont know why 4 is not working) (eeprom write int is taking 2 extra may be)

Here, deviceSLLength is made 10 because I have a device Serial Number auto generated which is a 10 DIGIT number (stored and used as char. hence 0-9 is my 10 digits and 10th one is \0.

Now, variables are:

//necessary variables for running config (via structure)
char devSL[deviceSLlength]; //10 digit device SL No including NULL char [0-9 total 10 + 1]
boolean optHWEn = false;
boolean debugEn = false;
int targetTemp = 4; //default
unsigned int deltaT = 4; //default Delta T = 4
unsigned int opsMode = 1; //default ops mode = 1
unsigned long compressorRunTime; //default 12
unsigned long defrostTime; //default 30
char userPass[userPasswordLength] = "1111"; //default user password is 1111

Hope this above part is self explanatory. The data types are made like this intentionally and not that willing to change them. The initial values are the default values, but when eeprom read will be done, then these will be changed for damn sure. Why, I will explain later. About initial values here given, they will give a hint what may be the value. Still for more hint, all unsigned values will NEVER be negative. Int (signed int) may be positive or negative, but value will be within -50 to +50 kind of thing. I checked, it needs 2 bytes with min/max of my possibilities.

The unsigned LONG variables here are actually storing simple integer, means they don't need that looong here, rather that same 2 byte. one will be max 24, and other will be max 30. The reason i declared this variable LONG because after reading from eeprom a 2 digit integer, which represents hours (hence default 12hrs) in storage (user inputs hours) and it is used by millis() function where it is multiplied by 3600000. Thus after reading from eeprom this same value 12 turns into more...

and the password: It's just though i am saying 4 digit PIN code, but it can be anything within 4 chars. So better let's stick with 4 digit, defauly hence 1111. So practically the variable needs 0-3 + 1 = 4 in size. (but if I dont use 5 as size, i face another issue, which started this thread. Will discuss later.)

My eeprom address map is below:

//EEPROPM Addressings need to be re-check for more optimization
#define eepromOffset 0 //[Address 0, reserved]
#define deviceSLAddr (eepromOffset + 1) //[Start at 1, used for device SL (10byte incl NULL), ends at 11]
#define optHWEn_eepromAddr (deviceSLAddr + 11) //[Start at 1+11=12, used for 1byte, ends at 12]
#define debugEn_eepromAddr (optHWEn_eepromAddr + 1) //[Start at 12+1=13, used for 1byte, ends at 13]
#define targetTemp_eepromAddr (debugEn_eepromAddr + 1) //[Start at 13+1=14, used for 2byte, ends at 15]
#define deltaT_eepromAddr (targetTemp_eepromAddr + 2) //[Start at 15+2=17, used for 1byte, ends at 17]
#define opsMode_eepromAddr (deltaT_eepromAddr + 1) //[Start at 16+1=17, used for 1byte, ends at 17]
#define compressorRunTime_eepromAddr (opsMode_eepromAddr + 1) //[Start at 17+1=18, used for 1byte, ends at 18] //we may avoid it and can put this in a variable as per update.
#define defrostTime_eepromAddr (compressorRunTime_eepromAddr + 1) //[Start at 18+1=19, used for 1byte, ends at 19]
#define userPassword_eepromAddr (defrostTime_eepromAddr + 1) //[Start at 19+1=20, used for 4byte, ends at 23]

Please consider, the manual calculation in comments are not accurate. The sizeof(varname) did not helped me that much I dont know why, but will make a further trial again surely.

Now, while inside the code, when first boot, i generate the DeviceSL using the function:

//Generate device Serial number
void generate_devSL(unsigned int a){
  if (EEPROM.read(0) == '#') {
    for (int i = 0; i < a; i++) {
      devSL[i] = EEPROM.read(i+1);
    }
  } 
  
  else {
    digitalWrite(systemStateLED, HIGH);
    //first flushing the entire EEPROM
    for (int i=0; i <= 255; i++){
      EEPROM.update(0, i);
      delay(5);
    }
    
    debug.println(F("Generating random Serial since first boot."));
    for (int i = 0; i < a; i++) {
      devSL[i] = TrueRandom.random(49, 58);
      debug.print(devSL[i]);
      EEPROM.update(i+1, devSL[i]);
      delay(100);
    }
    EEPROM.update(0, '#');
    debug.println();
    factoryReset();
    digitalWrite(systemStateLED, LOW);
  }
}

Hope this is self explanatory. Here, instead of PUT() using UPDATE() as that's almost serving the purpose.

The factory default setting function is below, which i use when i need the system to go back to factory default:

//The factory reset function that will reset EEROM data into default.
void factoryReset(void){
  debug.println(F("Resetting all values to factory default..."));
  digitalWrite(systemStateLED, HIGH);
  EEPROM.update(optHWEn_eepromAddr, 0);
  delay(5);
  EEPROM.update(debugEn_eepromAddr, 0);
  delay(5);
  //EEPROMWriteInt(targetTemp_eepromAddr, 4);
  EEPROM.update(targetTemp_eepromAddr, 4);
  delay(5);
  //EEPROMWriteInt(deltaT_eepromAddr, 4);
  EEPROM.update(deltaT_eepromAddr, 4);
  delay(5);
  EEPROM.update(opsMode_eepromAddr, 1);
  delay(5);
  EEPROM.update(compressorRunTime_eepromAddr, 12);
  delay(5);
  EEPROM.update(defrostTime_eepromAddr, 30);
  delay(5);
  //EEPROMWriteLong(userPassword_eepromAddr, 1111);
  userPass[userPasswordLength] = "1111";
  eeprom_write_string(userPassword_eepromAddr, userPass);
  delay(5);
  digitalWrite(systemStateLED, LOW);
  debug.println(F("Done!"));
  debug.println();
  loadBootConf();
  printBootConf();
  debug.print(F("Default User Password: ")); debug.println(userPass);
  soft_reset(3000);
}

Hope, the above is also self explanatory. Except that eepromWriteString function. That I am again posting here below:

// Writes a sequence of bytes to eeprom starting at the specified address.
// Returns true if the whole array is successfully written.
// Returns false if the start or end addresses aren't between
// the minimum and maximum allowed values.
// When returning false, nothing gets written to eeprom.
boolean eeprom_write_bytes(int startAddr, const byte* array, int numBytes) {
  // counter
  int i;
  int EEPROM_MAX_ADDR = (startAddr + 99); //This is the total byte (ADDR) allocation for one string. 99 bytes including the given one (addr) + 1byte NULL Terminator.
  // both first byte and last byte addresses must fall within
  // the allowed range 
  //if (!eeprom_is_addr_ok(startAddr) || !eeprom_is_addr_ok(startAddr + numBytes)) {
    //return false;
  //}
  if (!eeprom_is_addr_ok(startAddr, numBytes, EEPROM_MAX_ADDR)) { // check start address
    return false;
  }
  for (i = 0; i < numBytes; i++) {
    EEPROM.write(startAddr + i, array[i]);
  }
  return true;
}

// Writes a string starting at the specified address.
// Returns true if the whole string is successfully written.
// Returns false if the address of one or more bytes fall outside the allowed range.
// If false is returned, nothing gets written to the eeprom.
boolean eeprom_write_string(int addr, const char* string) {
  int numBytes; // actual number of bytes to be written
  //write the string contents plus the string terminator byte (0x00)
  numBytes = strlen(string) + 1;
  return eeprom_write_bytes(addr, (const byte*)string, numBytes);
}

0-3 + 1 = 4 Wrong !

Let's count out the characters
0
1
2
3 that's 4 so far
plus 1 for the terminating zero make 5

Why are you so determined to make things difficult when you could use a struct ?

From the above, I clearly get the output as desired, after calling the factory reset, eeprom updates and the results come as:

Hardware Serial: 8757767625

Boot configuration:
Optional Hardwares Enable: 0
Debug Enable: 0
Target Temp: 26C
Delta T: 4
Ops Mode: 1
Compressor Run Time: 12 Hrs
Defrost Time: 30 mins

Please note: I issued the config command to change the target temp to 26 instead of 4 and that also got stored. The C, Hrs, these are coming from serial.print() function.

//Function for loading the conf during boot. Also the same function is used to call the runtime conf after a new conf settings changed.
void loadBootConf(void){
  optHWEn = EEPROM.read(optHWEn_eepromAddr);
  debugEn = EEPROM.read(debugEn_eepromAddr);
  //targetTemp = EEPROMReadInt(targetTemp_eepromAddr);
  targetTemp = EEPROM.read(targetTemp_eepromAddr);
  deltaT = EEPROM.read(deltaT_eepromAddr);
  opsMode = EEPROM.read(opsMode_eepromAddr);
  compressorRunTime = (EEPROM.read(compressorRunTime_eepromAddr) * 3600000); //eeprom value in hours. Hence 3600000 ms
  //compressorRunTime = (EEPROM.read(compressorRunTime_eepromAddr) * 60000); //eeprom value in hours. Hence 3600000 ms
  defrostTime = (EEPROM.read(defrostTime_eepromAddr) * 60000); //eeprom value in minutes, hence 60000 ms
  eeprom_read_string(userPassword_eepromAddr, userPass, sizeof(userPass)+1);
}

//A fancy way of showing the running conf.
void printBootConf(void) {
  debug.println(F("Boot configuration:"));
  debug.print(F("Optional Hardwares Enable: ")); debug.println(optHWEn);
  debug.print(F("Debug Enable: ")); debug.println(debugEn);
  debug.print(F("Target Temp: ")); debug.print(targetTemp); debug.println(F("C"));
  debug.print(F("Delta T: ")); debug.println(deltaT);
  debug.print(F("Ops Mode: ")); debug.println(opsMode);
  debug.print(F("Compressor Run Time: ")); debug.print(compressorRunTime/3600000); debug.println(F(" Hrs"));
  debug.print(F("Defrost Time: ")); debug.print(defrostTime/60000); debug.println(F(" mins"));
  debug.println();
}

Hope this is also self explanatory.

Now, here is the thing, when I use the userPass[5] then opsMode gets a value. But when I make userPass[4] then opsMode gets 0 even if I store 2 or even 3, not just 1. And that's my FIRST problem, that I'm trying to fix!!

UKHeliBob:
Why are you so determined to make things difficult when you could use a struct ?

lol... because let me first fix it please... I already loved struct method... but first, please, let me fix this... I need to know what is causing this trouble...
And surely, one issue was updating ONLY one value (i know still struct can update ONLY the needed). Will come to that part I promise.

UKHeliBob:
0-3 + 1 = 4 Wrong !

Let's count out the characters
0
1
2
3 that's 4 so far
plus 1 for the terminating zero make 5

So, for "1234" as password 0th=1, 1st=2, 2nd=3, 3rd=4, 4th=NULL and hence char password[4] right?

aq_mishu:
So, for "1234" as password 0th=1, 1st=2, 2nd=3, 3rd=4, 4th=NULL and hence char password[4] right?

no, password[5].
arrays hold x number of elements, but those elements are 0-indexed.
char arrays (aka strings) need an extra element for the unseen null.
so, an array of 3 numbers is int numArray[3] = {1, 2, 3};
an array of 3 letters is char charArray[3] = {'a', 'b', 'c'};
a string of 3 letters is char myString[4] = "abc";//this is equivalent to {'a', 'b', 'c', '\0'}
numArray[0] holds the value 1, charArray[1] holds the value 'b' and myString[4] holds the value '\0'.

Perehama:
no, password[5].
a string of 3 letters is char myString[4] = "abc";//this is equivalent to {'a', 'b', 'c', '\0'}

So this means char variableName[5] means just 5 elements, element 0 to element 3 and element 4=NULL right?
this also means if I need 10char string, then declaration should not be variableName[10] rather variableName[11] right?