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

Generated on Fri Apr 22 06:54:10 2011 for SFB by doxygen 1.5.9