# eeprom4.cpp

The benefit of EEPROM is that it's persistent: It stores values indefinitely until they're changed. But, that's also a problem: If EEPROM data 'lasts forever', how and when do you initialize EEPROM data values? Notice, for example, how eeprom2::cpp never actually set its button-press-count to zero, to get things started cleanly! Bogus!

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
      }
    }