Does EEPROM.put support power-fail recovery?


#1

@rick
According to document EEPROM.put should support power-fail recovery.

https://docs.particle.io/reference/device-os/firmware/photon/#put-

If the Photon loses power before the write finishes, the partially written data will be ignored.

But I did not find any algorithm do power-fail recovery work,for example I call EEPROM.put(a float number) and power off when only two byte write back to flash,when I read this float number when power on again something will go wrong.

    // Writes a new value for a byte of EEPROM
    // Performs a page swap (move all valid records to a new page)
    // if the current page is full
    void put(Index index, Data data)
    {
        writeRange(index, &data, sizeof(data));
    }

    // Write each byte in the range if its value has changed.
    void writeRange(Index indexBegin, const Data *data, uint16_t length)
    {
        // don't write anything if index is out of range
        Index indexEnd = indexBegin + length;
        if(indexEnd > capacity())
        {
            return;
        }

        // Read existing values for range
        std::unique_ptr<Data[]> existingData(new Data[length]);
        // don't write anything if memory is full
        if(!existingData)
        {
            return;
        }

        Address writeAddressBegin;

        // Read the data and make sure there are no previous invalid
        // records before starting to write
        bool success = readRangeAndFindEmpty(getActivePage(),
                existingData.get(), indexBegin, length, writeAddressBegin);

        // Write records for all new values
        success = success && writeRangeChanged(writeAddressBegin, indexBegin, data, existingData.get(), length);

        // If any writes failed because the page was full or a marginal
        // write error occured, do a page swap then write all the
        // records
        if(!success)
        {
            swapPagesAndWrite(indexBegin, data, length);
        }
    }

    // Write new records backwards in Flash. This ensures data
    // consistency if writeRange is interrupted by a reset since reads
    // stop at the first non-valid record.
    bool writeRangeChanged(Address writeAddressBegin, Index indexBegin, const uint8_t *data, const uint8_t *existingData, uint16_t length)
    {
        bool success = true;

        // Count changed values
        uint16_t changedCount = 0;
        for(uint16_t i = 0; i < length && success; i++)
        {
            if(existingData[i] != data[i])
            {
                changedCount++;
            }
        }

        // Write all changed values, backwards from the end
        if(changedCount > 0)
        {
            Address writeAddress = writeAddressBegin + changedCount * sizeof(Record);
            Address endAddress = getPageEnd(getActivePage());

            // There must be an empty record after the position where the
            // last record will be written to act as a separator for the
            // valid record detection algorithm to work well
            if(writeAddress < endAddress)
            {
                Record separatorRecord;
                store.read(writeAddress, &separatorRecord, sizeof(separatorRecord));

                success = separatorRecord.empty();
            }

            for(uint16_t i = 0; i < length && success; i++)
            {
                if(existingData[i] != data[i])
                {
                    Index index = indexBegin + i;
                    writeAddress -= sizeof(Record);
                    success = success && writeRecord(
                            writeAddress, endAddress, Record(index, data[i]));
                }
            }
        }

        return success;
    }

One Record save one byte,So only one byte can be recovery after power-fail issue.Any type for example float more than one byte can not recovery after power-fail issue.Am I right?


#2

@ming I am using EEPROM with a Photon for exactly this purpose.

I do 4 things which make EEPROM easy to use:

  1. A Struct with all the data
struct mySettings
{
    byte    resumeFlag;
};
  1. create eeprom settings object called param mySettings param; so that if I want to use the values they are param.resumeFlag

  2. functions to put and get the struct from EEPROM

void putParameters()
{
    int addr = 0;
    EEPROM.put(addr, param);
}
  1. Take care to initialise the values when initially started, e.g. a setup

#3

I do the same.I am worry about power-fail recovery.
Reference Document:

The object data is first compared to the data written in the EEPROM to avoid writing values that haven't changed.

If the Photon loses power before the write finishes, the partially written data will be ignored.

But I do not think particle SDK source code support this feature


#4

Source code:


#5

@ming You have 2 strategies in my opinion:

  1. Do regular EEPROM put of the data you need to do a “resume” after power restored. Then there is the issue that your last commit of recovery data may have been a while ago but writing only when changes means you could call this every loop - assuming you are changing recovery values with less frequency.
  2. Catch the system event for power outage/brownout and do a put to EEPROM at that point (of course this may not complete).

A hardware solution is to have a coin cell battery powering VBAT and to use retained RAM to store your recovery data. Have you considered this?


#6

Yes.
But current issue is:
According to Particle Document EEPROM.put support power-fail recovery.
According to Particle SDK source code EEPROM.put do not support power-fail recovery If I am right.


#7

Suggest you PM Julian Vanier or Rick - there are numerous cases where the documentation is not 100% correct.


#8

Thanks.