But if you just initialize your EEPROM data in your setup() function, when you initialize reflexes and alarms and so on, well then that initialization will happen on every reboot, and the 'persistent' advantage of the EEPROM is lost.
One strategy is to create a special 'reset' reflex of some kind, that initializes EEPROM when handled. In a way, though, that doesn't solve the problem so much as just fob it off on somebody else: Now something external to the board must decide when EEPROM initialization should happen.
Another strategy is to write some 'magic value' along with the other EEPROM data; then later, if the magic value is found to be there, the rest of the written EEPROM data is assumed to be valid. A problem with that approach is that if the EEPROM data is only partially overwritten, then the magic value might still match even though the data is corrupt.
A third strategy is to write two complete copies of the data, in different places in EEPROM. If the two copies match, they are trusted, otherwise they are initialized. This doubles your space requirements, which in the worst case is like cutting your EEPROM in half, but if you can afford the space the 'two copies' approach is indeed quite robust.
This advanced sketch illustrates a fourth strategy for handling EEPROM initialization. Instead of writing all the data twice, the idea is to compute an SFBChecksum value from the EEPROM data, and write the checksum along with the data, at an overhead of only 8 bytes, no matter how much data is stored.
Then later, if that stored checksum matches a freshly-computed checksum of the EEPROM data, the data is taken as valid; if not, the data is initialized.
Used this way, the SFBChecksum acts like a magic value that depends on the stored data values -- so if any of the stored data is changed, it is likely (though not guaranteed) that the SFBChecksum won't match.
// Eeprom sketch 4: Use an SFBChecksum to automatically initialize // missing or invalid EEPROM data. #define DATA_ADDR 1234 struct MyPersistentData { // MyPersistentData is the actual data we care about u32 aInt; // Whatever you want float aFloat; // to save goes in u8 aByte; // here.. }; struct MyEepromData { // but MyEepromData is what we actually store in the EEPROM SFBChecksum cs; // It has a checksum.. MyPersistentData theData; // ..as well as the data we care about }; MyEepromData eepromBuf; // A place in RAM to hold the data void checksumData(SFBChecksum cs) { SFBChecksumInit(cs); // Init the checksum we were given, then add SFBChecksumAddBytes(cs, // the MyPersistentData part of eepromBuf to it (char*) &eepromBuf.theData, sizeof(eepromBuf.theData)); } void saveData() { // Here whenever we have new data to write checksumData(eepromBuf.cs); // First, get a valid checksum for the data eepromWrite(DATA_ADDR, (u8*) &eepromBuf, sizeof(eepromBuf)); // Then write everything } void loadOrInitialize() { eepromRead(DATA_ADDR, (u8*) &eepromBuf, sizeof(eepromBuf)); // Load eepromBuf SFBChecksum check; // Scratch checksum checksumData(check); // Compute checksum of existing EEPROM data if (!SFBChecksumEqual(check,eepromBuf.cs)) { // If stored CS doesn't match, initialize.. eepromBuf.theData.aInt = 100; // Some initial value for aInt eepromBuf.theData.aFloat = 1.234; // ditto eepromBuf.theData.aByte = '!'; // ditto saveData(); // Stash initialized values } } void setup() { setLogFace(ALL_FACES); // Send logging everyway loadOrInitialize(); // Go for EEPROM data } void loop() { if (buttonDown()) { // Report and update values logNormal("aInt=%d aFloat=%f aByte=%c\n", eepromBuf.theData.aInt, eepromBuf.theData.aFloat, eepromBuf.theData.aByte); ++eepromBuf.theData.aInt; // Make some changes eepromBuf.theData.aFloat = eepromBuf.theData.aInt/100.0; // Ditto saveData(); // Update checksum and store while (buttonDown()) delay(1); // Wait for button up } }