Confused about EEPROM functions

I have a project in which I need to store a setting value in EEPROM. Just one value-- and it's an integer. I've tried to follow the patterns of the Arduino Examples libraries for EEPROMUpdate and EEPROMGet, but the results I'm getting don't make sense.

I'm using an Arduino Nano Every. Here's my setup() code with some preamble:

#define DEBUGGING_MESSAGES_ENABLED
#ifdef DEBUGGING_MESSAGES_ENABLED
#define DebugMsg(x)     do { Serial.print(x); } while (0)
#define DebugMsgLn(x)   do { Serial.println(x); } while (0)
#else
#define DebugMsg(x)
#define DebugMsgLn(x)
#endif

void setLEDColor(const byte rgb[]) {
  analogWrite(ledRed,   rgb[P_RED]);
  analogWrite(ledGreen, rgb[P_GREEN]);
  analogWrite(ledBlue,  rgb[P_BLUE]);
}
int normalizeRelayTimerMinutes(int timerMinutes) {
  static const int minRelayTimerMinutes = 1;
  static const int maxRelayTimerMinutes = 20;
  timerMinutes = max(timerMinutes, minRelayTimerMinutes);
  timerMinutes = min(timerMinutes, maxRelayTimerMinutes);
  return timerMinutes;
}

void setup() {
  #ifdef DEBUGGING_MESSAGES_ENABLED
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect
  }
  // and delay an extra second, because Serial port isn't *really* connected correctly yet...
  delay(1000);
  #endif
  
  DebugMsgLn("PAT setup(): Initializing...");

  pinMode(ledRed, OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue, OUTPUT);
 
  digitalWrite(modeButton, LOW);
  pinMode(modeButton, INPUT);
  digitalWrite(tripButton, LOW);
  pinMode(tripButton, INPUT);
  digitalWrite(remoteButton, LOW);
  pinMode(remoteButton, INPUT);

  digitalWrite(relayControl, HIGH);  // relay is active-low; pullup to default as OFF
  pinMode(relayControl, OUTPUT);

  setLEDColor(color[RGB_WHITE]);
  delay(1000);

  static const int eeAddr = 10;
  int relayTimerValue = -42;        // something absurd, just to prove EEPROM.get() has effect
  EEPROM.get(eeAddr, relayTimerValue);
  DebugMsg("PAT setup(): relayTimerValue = "); DebugMsgLn(relayTimerValue);
  int relayTimerNormalValue = normalizeRelayTimerMinutes(relayTimerValue);
  if (relayTimerValue != relayTimerNormalValue) {
    DebugMsg("PAT setup(): non-normal value of relayTimerValue found in EEPROM: ");
    DebugMsg(relayTimerValue);
    DebugMsg("; NORMALIZED value: "); DebugMsgLn(relayTimerNormalValue);
    relayTimerValue = relayTimerNormalValue;
    EEPROM.update(eeAddr, relayTimerValue);
  }
  relayTimerMs = relayTimerValue * minToMs;
  DebugMsg("PAT setup(): relayTimerMs = "); DebugMsg(relayTimerMs); DebugMsgLn("ms");

  delay(1000);
  setLEDColor(color[RGB_OFF]);
  DebugMsgLn("PAT is READY.");
}

And here's the output on the Serial Monitor, generated by downloading the program with a new value for eeAddr, letting it run, then hitting reset after it completes, then doing that once again...

19:47:49.791 -> PAT setup(): Initializing...
19:47:50.771 -> PAT setup(): relayTimerValue = -1
19:47:50.806 -> PAT setup(): non-normal value of relayTimerValue found in EEPROM: -1; NORMALIZED value: 1
19:47:50.912 -> PAT setup(): relayTimerMs = 60000ms
19:47:51.888 -> PAT is READY.
19:48:11.894 -> PAT setup(): Initializing...
19:48:12.866 -> PAT setup(): relayTimerValue = -255
19:48:12.935 -> PAT setup(): non-normal value of relayTimerValue found in EEPROM: -255; NORMALIZED value: 1
19:48:13.010 -> PAT setup(): relayTimerMs = 60000ms
19:48:13.990 -> PAT is READY.
19:48:24.492 -> PAT setup(): Initializing...
19:48:25.488 -> PAT setup(): relayTimerValue = -255
19:48:25.522 -> PAT setup(): non-normal value of relayTimerValue found in EEPROM: -255; NORMALIZED value: 1
19:48:25.628 -> PAT setup(): relayTimerMs = 60000ms
19:48:26.604 -> PAT is READY.

I'm a bit lost... The value that is read seems to be -1 the first time (which I can see might make sense, if EEPROM is factory-initialized to 0xFFFFFFFFFFF....). But I change it to +1, and then STORE it. On reboot, I read it back as -255... Is there some data size issue at play here? I dont' get it....

Grateful for any hints...

Please read "Read this before posting a programming question" at the top of the forum.
Post all of the sketch- not just a snippet.

In the snip that you posted, this makes no sense:

 int relayTimerValue = -42;        // something absurd, just to prove EEPROM.get() has effect
  EEPROM.get(eeAddr, relayTimerValue);

You could have as easily written:

 int relayTimerValue;     
  EEPROM.get(eeAddr, relayTimerValue);

In your code you are declaring a type int, relayTimerValue, initially with a value of -42.
In the next line you are reading the byte at eeAddr and putting it into the variable relayTimerValue.

when you write to the epprom you are dealing with bytes by default. one byte can only hold a value between 0 and 255. in arduino land... int variables consist of two bytes to allow for bigger numbers. you would need to google the conversion or just simply google "how to write an int to eeprom".

Next time, please try to compile the code you post. It took me too many minutes to trim your code down to the point where it would compile.

Interesting problem you have there. You are using a method defined for 8-bit bytes and giving it a 16-bit integer. The compiler should complain about that. I tried it several times on my system with all compiler warnings enabled and it didn't squeak.

EEPROM read, write and update only work on bytes. They cannot use ints. Those are old methods that are really only kept in the library for compatibility with old sketches.

The newer methods get and put will work on any data of any size (so long as the size is known at compile time.)

SteveMann:
Post all of the sketch- not just a snippet.

Apologies. The entire sketch is 500 lines -- and the majority of that has nothing to do with reading or writing to EEPROM. What I should have done is create a simple sketch that only does the reading and writing, to encapsulate the question. I do apologize for wasting your time.

SteveMann:
In the snip that you posted, this makes no sense:

int relayTimerValue = -42; // something absurd, just to prove EEPROM.get() has effect
EEPROM.get(eeAddr, relayTimerValue);

You could have as easily written:

int relayTimerValue; EEPROM.get(eeAddr, relayTimerValue);

I assumed the comment would make my rationale for pre-initializing the variable clear. I wanted to have decent odds of determining whether the call to EEPROM.get() actually modified my variable. If I left it uninitialized, I wouldn't be able to tell stack garbage from EEPROM garbage. This strategy worked -- after then get() call, the value was changed, so I can be certain that the get() changed my value.

SteveMann:
In your code you are declaring a type int, relayTimerValue, initially with a value of -42.
In the next line you are reading the byte at eeAddr and putting it into the variable relayTimerValue.

Perhaps I misunderstood the example sketches, but it seemed reasonably clear to me that get() and put() can move entire data structures of arbitrary size in and out of EEPROM in a single call -- To wit, this snip appears in the EEPROMGet Example code at https://www.arduino.cc/en/Tutorial/EEPROMGet:

struct MyObject {
  float field1;
  byte field2;
  char name[10];
};

void secondTest() {
  int eeAddress = sizeof(float); //Move address to the next byte after float 'f'.

  MyObject customVar; //Variable to store custom object read from EEPROM.
  EEPROM.get(eeAddress, customVar);

  Serial.println("Read custom object from EEPROM: ");
  Serial.println(customVar.field1);
  Serial.println(customVar.field2);
  Serial.println(customVar.name);
}

read() and write(), on the other hand, appear to be single-byte I/O.

MorganS:
Next time, please try to compile the code you post. It took me too many minutes to trim your code down to the point where it would compile.

The code I posted does compile, but probably not by itself. Again, I do regret wasting your time-- I should have been more considerate when I posted that code. Please accept my apologies.

MorganS:
EEPROM read, write and update only work on bytes. They cannot use ints. Those are old methods that are really only kept in the library for compatibility with old sketches.

The newer methods get and put will work on any data of any size (so long as the size is known at compile time.)

AHA -- I misunderstood the example for EEPROM.update() (which is absolutely not clear, by the way!). Since the example showed a call where an int was passed (not a byte), I thought update() behaved like get() and put(). Seems that is not the case. Odd though -- my code is actually writing a 1, which, like the update() example, is less than 256, so the example code suggests my code should still have worked.... Specifically, the EEPROMUpdate example code here https://www.arduino.cc/en/Tutorial/EEPROMUpdate, shows this:

void loop() {
  /***
    need to divide by 4 because analog inputs range from
    0 to 1023 and each byte of the EEPROM can only hold a
    value from 0 to 255.
  ***/
  int val = analogRead(0) / 4;

  /***
    Update the particular EEPROM cell.
    these values will remain there when the board is
    turned off.
  ***/
  EEPROM.update(address, val);

I don't understand the difference, or why this strategy is failing in my code. But it doesn't matter -- I do plan to store values larger than 255, so I swapped out the update() call for a put() call, and my code now works correctly. :slight_smile:

Just between us, I wish the library API reference were clearer. I haven't seen a lot of specific detail about the exact API (data types, limitations, behavior) -- just a nice fluffy blurb followed by a contrived example, from which we are all expected to divine the salient behavioral details! ::slight_smile: I suppose I could blame the IDE as well, since it doesn't offer any warnings about these types of truncations. Odd, since the underlying compiler is certainly capable... Lesson learned-- the online reference docs are just barely useful, and I need to consult the headers (and possibly the implementation) for accurate details.

In any case, thank you for having the patience to work through my bad question, and for giving me the hints I needed! Much appreciated.

BTW, I have the answer I needed, but I've posted the entire sketch on my GitHub repo, in case it is still of interest.

EEPROM.update() works on bytes, not ints. Use EEPROM.put() to write and EEPROM.get() to read; those functions work on any type of data.

Yes, I went to the online documentation first to answer your question, then I opened up the source code on my computer to be sure. The buck stops at the source code. That's why we like open-source.

chris_atx:
What I should have done is create a simple sketch that only does the reading and writing, to encapsulate the question. I do apologize for wasting your time.

Please accept my apologies.

Apology accepted, but not needed. I had a program of over 1,000 lines and 12 Tabs, and in trying to ask a question I did make a short, simple test sketch. When I did, my problem really stood out before posting the question.