Reading and Writing Structs to and from Arduino's EEPROM

13.4k views Asked by At

I'm trying to write data structures defines in C to my Arduino Uno board's non-volatile memory, so the values of the struct will be retained after the power goes off or it is reset. To my understanding, the only way to do this (while the sketch is running) would be to write to arduino's EEPROM. Although I can write individual bytes (sets a byte with value 1 at address 0):

eeprom_write_byte(0,1);

I am stuck trying to write a whole struct:

typedef struct NewProject_Sequence {
    NewProject_SequenceId sequenceId;
    NewProject_SequenceLength maxRange;
    NewProject_SequenceLength minRange;
    NewProject_SequenceLength seqLength;
    NewProject_SceneId sceneList[5];
} NewProject_Sequence;

Because of the EEPROM's limit of 100,000 writes, I don't want to write to the Arduino in a loop going through each byte, for this will probably use it up pretty fast. Does anyone know a more efficient way of doing this, either with EEPROM or if there's a way to write to PROGMEM while the sketch is running? (without using the Arduino Library, just C).

RESOLVED I ended up writing two custom functions -- eepromWrite and eepromRead. They are listed below:

void eepromRead(uint16_t addr, void* output, uint16_t length) {
    uint8_t* src; 
    uint8_t* dst;
    src = (uint8_t*)addr;
    dst = (uint8_t*)output;
    for (uint16_t i = 0; i < length; i++) {
        *dst++ = eeprom_read_byte(src++);
    }
}

void eepromWrite(uint16_t addr, void* input, uint16_t length) {
    uint8_t* src; 
    uint8_t* dst;
    src = (uint8_t*)input;
    dst = (uint8_t*)addr;
    for (uint16_t i = 0; i < length; i++) {
        eeprom_write_byte(dst++, *src++);
    }
}

The would be implemented like this:

uint16_t currentAddress;
struct {
    uint16_t x;
    uint16_t y;
} data;

struct {

} output;
uint16_t input

eepromWrite(currentAddress, data, sizeof(data);
eepromRead(currentAddress, output, sizeof(data));
2

There are 2 answers

0
mpflaga On

Several solutions and or combinations.

  1. setup a timer event to store the values periodically, rather then back to back.
  2. use a checksum, then increment the initial offset, when writing. Where when reading you attempt each increment until you have a valid checksum. this spreads your data across the entire range increasing your life. modern flash drives do this.
  3. Catch the unit turning off, by using an external Brown Out Detector to trigger an INT to then quickly write the EEPROM. Where you can then also use the internal BOD to prevent corruption, before it falls below safe writing voltages. By having the external significantly higher than the internal thresholds. The time to write before complete shutdown can be increased by increasing the VCC capacitance. Where the external BOD is compared before the VCC and not directly the VCC itself.

crude external BOD circuit on ATmega

Here is a video explaining how to enable the internal BOD, for a ATtiny, where it is nearly identical for the other ATmega's. Video

0
hsaturn On

The Arduino EEPROM library provides get/put functions that are able to read and write structs...

Link to EEPROM.put(...)

The write is made only when a byte has changed.

So, using put/get is the solution to your problem.

I'm using these in a wide (25k) project without any problem.

And as already said I've used a timer to write not each time but some time to times.

Turning off detection is also a very good way to do this.