Extended EEPROM library for Arduino

| 65 Comments

For my ongoing clock project, I want to persistently store some data. That is, store data that is retained after turning off the Arduino. The processor on the Arduino board comes with on-board EEPROM. In the case of the Arduino Uno, the processor is the Atmega328, equipped with 1 glorious KByte of EEPROM memory.

The AVR libraries that come with the ATmega implements a relatively broad set of functions for reading, writing and management of the EEPROM (for a description see the AVR user manual). However, the Arduino standard EEPROM library exposes only functionality for reading and writing a single byte, as described here.

This is why I wrote the EEPROMex library, an extension of the standard Arduino EEPROM library. It writes and reads basic types like bytes, longs, ints, floats & doubles. It can also read/write single bits, arbitrary data-formats and arrays. It adds debug functionality to identify and stop writing outside of the EEPROM memory size and excessive writing to prevent memory wear.

The library

First of all, the library can be downloaded here:

A zipped tarball of this version can be found here.

The library starts by implementing the functions as present in the default EEPROM library, so it is fully compatible. You only need to change #include <EEPROM.h> to #include <EEPROMex.h>. The following example will work with both the standard and extended EEPROM library:

Writing different data formats

The aim of the library is to also support other standard data types: it currently implements writing and reading to int, long, float and double.

For reading:
uint8_t read(int address);
uint8_t readByte(int address);
uint16_t readInt(int address);
uint32_t readLong(int address);
float readFloat(int address);
double readDouble(int address);

Where address is the starting position in EEPROM, and the return value the value read from EEPROM. For writing:
bool write(int address, uint8_t value);
bool writeByte(int address, uint8_t value);
bool writeInt(int address, uint16_t value);
bool writeLong(int address, uint32_t value);
bool writeFloat(int address, float value);
bool writeDouble(int address, double value);

Where the return value indicates if the write has been successful. Note that readByte and writeByte are the same as read and write and have only been added for naming consistency. So let us now try the following example :

Update data

The write function of the AVR library that we are using will always overwrite the existing EEPROM value, even if it is equal to the one already present. Overwriting this cell has no practical use, but will increase EEPROM wear. To solve this, I added update functionality. The update functions are different from the write functions, in that they will check per byte if the current value differs and only update the the cell with a different value. This will not only reduce wear, and can also significantly reduce write time.

bool update(int address, uint8_t value);
bool updateByte(int address, uint8_t value);
bool updateInt(int address, uint16_t value);
bool updateLong(int address, uint32_t value);
bool updateFloat(int address, float value);
bool updateDouble(int address, double);

Efficient storage, writing single bits

Depending on your requirements, you may want to be more efficient in the way you store certain values. This is particularly relevant if you want to store Boolean values: As you need only one bit to store Boolean, you can store 8 Booleans in a single byte.

The following functions implements reading and writing single bits:
bool readBit(int address, byte bit)
Where bit is the write position in the byte, ranging from [0..7], with bit 0 being the right-most. The return value is the read bit.
bool writeBit(int address, uint8_t bit, bool value)
bool updateBit(int address, uint8_t bit, bool value)

Writing data blocks

We can generalize our writing and reading functions so that they can handle any kind of data structure :

int readBlock(int address, const T& value)
int writeBlock(int address, const T& value)
int updateBlock(int address, const T& value)

where T is the type of the data to read/write/update. This can be a basic type, but also a more complex type like a struct. The return value gives the number of bytes that have been read, written or updated.

The library also supports reading and writing of arrays of a any kind of data structure. We can, for exampe, write an arrays of chars (a string), but we can also read/write, for example, arrays of structs.

int readBlock(int address, const T[]; value, int items)
int writeBlock(int address, const T[]; value, int items)
int updateBlock(int address, const T[]; value, int items)

Let’s have a look at the following example:

Address allocation

Typically, we write and store data to the EEPROM using fixed addresses, and it often proves to be a hassle to maintain them properly, such that there are not overwrites nor gaps. To simplify bookkeeping of addresses you can use the following function.

int getAddress(int noOfBytes);

The input noOfBytes indicates how many bytes your data structure are needed, the output is the first address available for writing in EEPROM. As long as you use this function to assign addresses to your variables, there will be no overlap between variables. This function is not much more than an every increasing counter and it differs from a malloc call in that there is no way to free memory. It is, however completely deterministic, which means that if acquire your address at the start of your applications, before any loops or conditional statements, every memory pointer will be the same after each reboot of the board.

Debugging options

The processor documentation (which can be found here , section 8.4) says the following about EEPROM:

The ATmega48A/PA/88A/PA/168A/PA/328/P contains 256/512/512/1Kbytes of data EEPROM memory. It is organized as a separate data space, in which single bytes can be read and written. The EEPROM has an endurance of at least 100,000 write/erase cycles.

This is may sound like a large number, but in theory you could burn out a memory cell in a few minutes: a write/erase cycle takes approximately 4 ms, so writing 100.000 times to a single cell takes 6 1/2 min. While there are some tests show much more writes before failure, they do not take into account reduction of retention time (see this article on EEPROM failure in space).

Notice how the documentation talks about write/erase rather than write. EEPROM programming consists of erasing a block (in our case a block of 8 bits, 1 byte), which means setting all bits to 0, and subsequently writing the bits that should become 1. It is the erase step that causes wear to the memory. This means that in theory we can separate the two, and overwriting an EEPROM value from 170, in bit format 10101010 to 235, in bit format 11101011 would only require writing the 2 specific bits, without erasing and causing wear. While this is possible (see the manual, section 8.6.3) it has not been implemented in the AVR function underpinning this library. (This library does not implement it either, but delivers similar functionality on byte scale using the update functions).

As said, it is easy to burn out a memory cell in few minutes, so while debugging your application it would be very useful to limit the number of allowed writes. It is easy to put a bracket at the wrong location, and placing an EEPROM write inside of a loop, rather than outside, and introduce extensive writing causing wear. The following function helps limit the number of writes.

setMaxAllowedWrites(int allowedWrites);

More writes than allowed will be refused and result in an error message. You can also set the address range used by the library:

setMemPool(int base, int memSize);

The lower value is used by the getAddress function, the upper value is used for setting the EEPROM size. Writing outside the maximum size will result in an error message. The following EEPROM sizes are predefined

Based on processor:

  • EEPROMSizeATmega168
  • EEPROMSizeATmega328
  • EEPROMSizeATmega1280

Based on board:

  • EEPROMSizeUno
  • EEPROMSizeUnoSMD
  • EEPROMSizeLilypad
  • EEPROMSizeDuemilanove
  • EEPROMSizeMega
  • EEPROMSizeDiecimila
  • EEPROMSizeNano

Note that these errors will only occur when _EEPROMex_DEBUG is enabled in EEPROMex.cpp. if not enabled, the checks will be optimized away during compilation. This reduced the performance overhead and memory usage of the library, so it is encouraged to disable the function after debugging.

EEPROM performance

All of the read/write functions first make sure the EEPROM is ready to be accessed. Since this may cause long delays if a write operation is still pending, time-critical applications should first poll the EEPROM e. g. using the isReady function before attempting any actual I/O:

bool isReady();

Let’s end with a slightly more extensive example that implements this and combines it with a few other functions. This example demonstrates the write speed and read speeds of the EEPROM memory:

timing1 Extended EEPROM library for Arduino image

Write, read and wait time EEPROM

The output of this sketch shows us a few things:

  • Writing one byte does not cost time (at least less than 1 ms), but it takes the EEPROM 4 to be ready for next write).
  • As a result, writing more than one byte will take more time. I would have expected writing a 4 bytes Long to take 3*4=12 ms, with a 4 ms recovery time.
  • Reading data from EEPROM does not cost significant time, and it does not take time for the EEPROM to recover.
  • When writing with the updateBlock instead of the writeBlock function the updateBlock is comparable when all bytes need to be changed and faster if less bytes need updating.

So, that’s it for now. Good luck using the library and let me know what you find!

65 Comments

  1. Sir, How should i go for MultiDimension Array
    like
    int TimeTable[Max_Periods][OnHours,OnMins,Duration];
    for reading and writing

  2. Hi,
    I really like your EPROM extende library and have tried now for a while to get it to work bu i get the same error all the time.
    I use Arduino 1.0 and when i compile the example i get the error:
    G:\arduino\arduino-1.0\libraries\EEPROMEx/EEPROMex.h:159: error: conflicting declaration ‘EEPROMClassEx EEPROM’
    G:\arduino\arduino-1.0\libraries\EEPROM\EEPROM.h:32: error: ‘EEPROM’ has a previous declaration as ‘EEPROMClass EEPROM’

    any pointers?

    best regards
    chris

    • Hi Chris,

      Can you include a sample sketch that creates this issue?

    • I think this is cause by the spelling error, it disappears after changing #include to .
      I’m using the example code provided with the library with IDE 1.0.3
      After the change I get the error “‘EEPROM’ was not declared in this scope”

      so, I included the standard eeprom library (I assume its unnecessary?) regardless.. error is

      ‘class EEPROMclass’ has no member named ‘writeInt’

      I’m a greenhorn.. the only assumption I can make from here is there is a problem in addressing the class, could this also be due to the typo?

      • haha… of course html would hide what I said… correction ” I changed include EEPROMex to EEPROMEx” when I referred to a typo

        • I got it, and realized that this was already mentioned below.
          Christian, in your arduino folder /libraries/EEPROMex/
          EEPROMex.cpp just needs the typo edited.
          Which Roland mentions in a comment a few below this one.

          // #include “EEPROMEx.h”
          #include “EEPROMex.h”

  3. The “setMaxAllowedWrites(int allowedWrites);” is here an int.
    Is it a short or long int?

    • According to the Arduino website:

      On the Arduino Uno (and other ATMega based boards) an int stores a 16-bit (2-byte) value. … On the Arduino Due, an int stores a 32-bit (4-byte) value.

      I will remove the ambiguity in the next version

  4. Great library, really made life easier for me today, i thought i had to write all this functions by myself til i found this, thanks

  5. hey there is a typo in EEPROMex.cpp

    // #include “EEPROMEx.h”
    #include “EEPROMex.h”

  6. i am getting an error not sure what to fix
    byte MO [21]; declared
    in setup vod is

    EEPROM.readBlock(0, MO,21);

    error is
    C:\Users\robert\Documents\Arduino\libraries\EEPROMEx/EEPROMex.h: In member function ‘int EEPROMClassEx::readBlock(int, const T*, int) [with T = byte]‘:
    CMSBridge_ino2timing_changes.ino:81: instantiated from here
    C:\Users\robert\Documents\Arduino\libraries\EEPROMEx/EEPROMex.h:92: warning: comparison between signed and unsigned integer expressions

  7. I have an array that is too large to fix in ram. Is there a way to address aspect of the array in EEPROM without reading it into or writing it from ram?
    EEPROM.writeBlock(address, “int array[122]=3245″, 122);
    the above line obviously doesn’t work but I need change that aspect of the array in that way, directly in ee
    having it in ram causes issues…Using PROGMEM an transferring it over is great
    except for the fact that it is a one off type of thing and the array values need to be changed as the program progresses

    • I have not tried such a thing, but I think you are on the right track: First push all your initial values in Flash using PROGMEM and copy these values during first start-up to EEPROM. You probably want to move these values only once to EEPROM, and afterwards start using the EEPROM values. You can do that in at least two ways: 1. Make a dedicated program that that only sets the initial values and run that once. Next start using your main program. 2. In you main program, use an initializer variable that you write to EEPROM at the beginning of the sequence. If you start the program it will check if this initializer is set, and if not: write Flash to EEPROM.

      Let me know what comes out!

  8. I am getting the following error’s implementing your example:
    ——————————————————————————————————–
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx/EEPROMEx.h:23:20: warning: EEPROM.h: No such file or directory
    EEPROM.ino: In function ‘void setMempoolEx()’:
    EEPROM:30: error: expected primary-expression before ‘=’ token
    EEPROM:30: error: expected primary-expression before ‘,’ token
    EEPROM:30: error: expected `;’ before ‘)’ token
    EEPROM.ino: In function ‘boolean loadConfigEx()’:
    EEPROM.ino:37: warning: comparison with string literal results in unspecified behaviour
    ———————————————————————————————————-
    As i havent included EEPROM.h becourse it should not be.
    Maybe there is a other error?

  9. Please discard the earlier message.
    But i stil get the following error while compiling:
    In file included from C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp:23:
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\/EEPROMEx.h:23:20: warning: EEPROM.h: No such file or directory
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp: In member function ‘bool EEPROMClassEx::writeBit(int, uint8_t, bool)’:
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp:148: warning: no return statement in function returning non-void
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp: In member function ‘bool EEPROMClassEx::updateBit(int, uint8_t, bool)’:
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp:203: warning: control reaches end of non-void function
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp: In member function ‘bool EEPROMClassEx::writeBit(int, uint8_t, bool)’:
    C:\Users\h.arends\Documents\Arduino\libraries\EEPROMEx\EEPROMEx.cpp:148: warning: control reaches end of non-void function

  10. Another typo/bug in EEPROMex.cpp
    Line 197:
    byteValOutput &= !(1 << bit); //Set bit to 0

    should be:
    byteValOutput &= ~(1 << bit); //Set bit to 0

    Using logical not clears the entire byte, not just that bit.

    Also, is the code in a repository somewhere? I've got a few enhancements I'd like to make when I have some time.

  11. Hi Thijs, I’ve a question about saving/updating a quite complex Structure, without loosing data by overwrtie:
    I have an Array (
    arr[1]{time1=1200,time2=1400,duration=30,min=10,max=50}
    ) and I can edit these in a menu. Now I wanna load the values after reset. So I call updateBlock(4,arr) to save and readBlock(4,arr) to load after reset. But I wanna make it easy to edit for other users and maybe someone saves something at adresse 2 and 21. When the array is to big it overwrites stuff. How can I avoid this without telling the user don’t save before adresse 200 or after 2 to have room to extand my array?
    Is it better to save the whole config in a Struct and update this by calling it once?
    Hope you have a hint to point me into right direction.

    THX Moritz

    • Hi Moriz,

      I’m afraid that I do not fully understand you question, but I’ll make a guess:

      Storing an array of structs should not be an issue, you can store an array of any type. I suppose, however, you do not want to store the full array if just 1 struct has changed. This is particularly smart, because of the limited writes an EEPROM can handle.

      This means that you have to manage the addresses yourself. This is not that difficult. The address would be something like:
      Adress = StartAdress + itemNo * sizeof(structType)

  12. Hi,

    I found your library and it looks great since its doing exactly what i needed. But i have a problem, i tried uploaded one of the example (EEPROMbackup) after download it erliear today and it gives a lot of error. I’m new to arduino and coding and would appreciate your help with the error and the reason for them.
    Here are the errors:

    EEPROMBackup:25: error: variable or field ‘digitalClockDisplay’ declared void
    EEPROMBackup:25: error: ‘time_t’ was not declared in this scope
    EEPROMBackup:25: error: ‘time_t’ does not name a type
    EEPROMBackup:26: error: ‘time_t’ does not name a type
    EEPROMBackup.pde: In function ‘bool CompareFunc(const void*, const void*)’:
    EEPROMBackup:39: error: expected initializer before ‘*’ token
    EEPROMBackup:40: error: expected initializer before ‘*’ token
    EEPROMBackup:43: error: ‘candidate’ was not declared in this scope
    EEPROMBackup:43: error: ‘minTime’ was not declared in this scope
    EEPROMBackup:43: error: ‘maxTime’ was not declared in this scope
    EEPROMBackup:48: error: ‘candidate’ was not declared in this scope
    EEPROMBackup:48: error: ‘current’ was not declared in this scope
    EEPROMBackup.pde: At global scope:
    EEPROMBackup:53: error: expected constructor, destructor, or type conversion before ‘<' token
    EEPROMBackup.pde: In function 'void setup()':
    EEPROMBackup:70: error: 'timeBackup' was not declared in this scope
    EEPROMBackup:70: error: 'minTime' was not declared in this scope
    EEPROMBackup:71: error: 'setTime' was not declared in this scope
    EEPROMBackup.pde: In function 'void loop()':
    EEPROMBackup:77: error: 'timeBackup' was not declared in this scope
    EEPROMBackup:77: error: 'now' was not declared in this scope
    EEPROMBackup:78: error: 'digitalClockDisplay' was not declared in this scope
    EEPROMBackup.pde: At global scope:
    EEPROMBackup:82: error: variable or field 'digitalClockDisplay' declared void
    EEPROMBackup:82: error: 'time_t' was not declared in this scope

    (of course i checked for the typo)

    Thankx for your help.

  13. You rock dude! Your library worked great for me and saved me hours of work. Thanks!

  14. Hi Thijs,
    I went through the examples and looks it ll make my work much simple.

    I have a very requirement of the very first comment, Will this work for multi dimensional array?

    Timer[nth timer][on time, off time]

    struct Timer_group {
    int hr_start[8];
    int sc_start[8];
    int hr_end[8];
    int sc_end[8];
    };

    Timer_group timer[40];

    • Sorry, I have not added support for multi-dimensional arrays. Your second example will probably work, though. If SizeOf(Timer_Group) gives you back the correct size (4*8*sizeOf(int)) it will work fine.

  15. Thanks Thijs.
    I went on tried that way, when I use two structures it doesnt seem to work as I expected. Please can you help debug?

    #include

    int addresskeyvalue = 0, addressnamejob=0, address=0;

    struct pair1 {
    int key;
    int value;
    };
    pair1 keyValueInput[2], keyValueInputx[2];

    struct triplet1 {
    int name;
    int job;
    int comp;
    };
    triplet1 namejobInput[2], namejobInputx[2];

    void setup()
    {

    int i;
    Serial.begin(9600);

    EEPROM.setMaxAllowedWrites(100);
    for(i=0;i<2;i++)
    {
    keyValueInput[i].key =i;
    keyValueInput[i].value=i+10;

    namejobInput[i].name=i+20;
    namejobInput[i].job=i+30;
    namejobInput[i].comp=i+40;

    }

    for(i=0;i<2;i++)
    {

    Serial.print(keyValueInput[i].key);
    Serial.print("\t");
    Serial.print(keyValueInput[i].value);
    Serial.print("\t");
    Serial.print(namejobInput[i].name);
    Serial.print("\t");
    Serial.print(namejobInput[i].job);
    Serial.print("\t");
    Serial.print(namejobInput[i].comp);
    Serial.println("");
    }

    Serial.println("after");
    Serial.println("");

    Serial.print(address);
    Serial.print("\t");
    EEPROM.updateBlock(address, keyValueInput,2);
    Serial.print(address);
    EEPROM.updateBlock(address, namejobInput,3);
    Serial.print("\t");
    Serial.print(address);
    Serial.println("");

    EEPROM.readBlock(address, keyValueInputx,2);
    EEPROM.readBlock(address, namejobInputx,3);

    for(i=0;i<2;i++)
    {

    Serial.print(keyValueInputx[i].key);
    Serial.print("\t");
    Serial.print(keyValueInputx[i].value);
    Serial.print("\t");
    Serial.print(namejobInputx[i].name);
    Serial.print("\t");
    Serial.print(namejobInputx[i].job);
    Serial.print("\t");
    Serial.print(namejobInputx[i].comp);
    Serial.println("");
    }

    }

    void loop() { }

  16. hi.thanks for library.can we use this library with i2c eeprom? thanks

    • Hi,

      I am working on a library that allows writing to different backends. Unfortunately, I do not have a i2C eeprom. How good are your programming skills? Maybe you can debug a first draft?

  17. I think I got the hang now, not sure if its intended way. Adding these lines for “address” makes it work as needed

    EEPROM.updateBlock(address, keyValueInput,2);
    address=sizeof(keyValueInput);
    Serial.print(address);
    EEPROM.updateBlock(address, namejobInput,3);
    Serial.print(“\t”);
    Serial.print(address);
    Serial.println(“”);
    address=0;
    EEPROM.readBlock(address, keyValueInputx,2);
    address=sizeof(keyValueInput);
    EEPROM.readBlock(address, namejobInputx,3);

  18. Hi!

    Found an error causing exceeding write limit and messages during “readBlock” operations – it because “isWriteOk” check used instead of “isReadOk” in “readBlock” function (template).
    Also I’ve got write limit exceeding messages when I’m writing (updating) 8 byte x 8 array by “EEPROM.updateBlock( EEPROMaddr.a_Sensors + ((settingNum-1)*8), pDeviceAddress + ((_tempID-1)*8), 8);” with limit of 100 write cycles… not sure why.
    To wish list – count only real write cycles in “update” functions.

  19. Hi Thuis,

    Great work! I like your library but I have one question about storing char * in EEPROM.

    I have a struct:

    struct Instellingen {
    unsigned int controllerID;
    long timedLoopInterval;
    long maintenanceLoopInterval;
    int timeSyncInterval;
    long timeZoneOffset;
    char *NTPPool;
    char *controllerCredentials;
    };

    The 2 char* fields may hold varying legths of character strings. The lengths for these fields are not known in advance.

    I would like to:
    int addressInstellingen;
    Instellingen instellingen;

    addressInstellingen = EEPROM.getAddress(sizeof(Instellingen));

    EEPROM.writeBlock(addressInstellingen, instellingen);
    and
    EEPROM.readBlock(addressInstellingen, instellingen);
    and
    EEPROM.updateBlock(addressInstellingen, instellingen);

    How do I have to deal with the varying lengths of the 2 char* fields in this struct?

    With regards,

    Jack.

    • Hi,
      The getAddress function is not really suitable for dynamic arrays. One of the prerequisites is that after each reboot the same adresses are calculated for the same variables. In case of dynamic variables you need to make your own implementation (and perhaps store length in the EEPROM as a header to the array.
      Also, the struct that you describe does not actually contain the array, only a pointer to the array (which is fixed size). Not efficient, but the easiest way to go about this is to define a fixed array that is always large enough.
      In fact, many people consider dynamic memory allocation on an embedded processor a Bad Idea, see for example this discussion: http://stackoverflow.com/questions/1725923/how-bad-is-it-to-use-dynamic-datastuctures-on-an-embedded-system

  20. There is a small mistake in updateBit: should use bitwise NOT (~) instead of logical NOT (!)

  21. Dear Thijs,

    I downloaded the latest Code release for EEPROMex from the GitHub Repository. Within the files there is a sketch example of EEPROMVar. However, I cannot get it to compile on Arduino IDE 1.0.5. I allways get the following error message:

    In file included from EEPROMVar.pde:10:
    C:\Program Files\Arduino\libraries\EEPROMex/EEPROMVar.h: In constructor ‘EEPROMVar::EEPROMVar(T)’:
    C:\Program Files\Arduino\libraries\EEPROMex/EEPROMVar.h:34: error: ‘class EEPROMClassEx’ has no member named ‘getAdress’

    Do you have any suggestions why that is? I´m clueless.

    THX so much,
    Jan

  22. Hi,
    Thanks for your work,
    I am only saving 1 float to EEPROM. I only am able to access 512 on the nano with the mega 328. Using the standard library I am able to access all 1024 bytes on the same board.
    Thanks for any help.

    Miles

  23. if you want to work with arrays then you can use templates:

    template
    inline bool blImage5::Wrap(blDataType (&StaticArray)[Size1][Size2])
    {
    // Do stuff with the
    // array elements
    for(int i = 0; i < Size1; ++i)
    {
    for(int j = 0; j < Size2; ++j)
    {
    }
    }
    }

  24. Hello!

    when i wrote with eepromex.h:

    EEPROM.writeFloat((600),55);

    the Memory 600 is empty

    when i wrote with eeprom.h:

    EEPROM.write((600),55);

    the Memory 600 has the Value 55.

    What i do false?

    Board ist Arduino Mega 2560

    Thank you for help and sorry for the English…

  25. I want to store some Boolean value .
    First question :
    If I store them as Struct and then use writeBlock it will know that is Boolean and stores is as Bit’s in a byte?
    [code]
    struct StoreSettings{
    boolean statusIDs[2];
    } storage = {
    {false,false}
    };
    [/code]

    Second Question:
    If I use writeBit(int address, uint8_t bit, bool value) and I write 7 Boolean values on a byte , what is the EEPROM wear ? it only wears that bit or it’s for the entire byte.

    • 1) Yes-ish. That is to say, writeBlock does not know of the internal structure of the struct, but only the bytes it uses in memory. These bytes are then copied to the EEPROM. Even if your struct uses only bitfields, the data is padded to a byte.
      2) I looked at the datasheets and the avr library when I wrote the library. There is no way of addressing individual bits, only bytes. It could be that the ATMega does something smart internally, but I do not believe so. There is nothing to indicate this in the datasheets

  26. Pingback: Drawing with a laser | Leaping into the future

  27. 1) is it neccessary to do
    EEPROM.setMemPool(memBase, EEPROMSizeUno);
    EEPROM.setMaxAllowedWrites(maxAllowedWrites);
    only in the setup() or also in the loop()?
    2) I am using the EEPROMex library to store RFIDs in the EEPROM. The sketch is
    used for an RFID Access Control. It adds RFIDs to the EEPROM that have access right,
    all other RFIDs read by the card reader that are not stored in EEPROM are denied access.
    It is also possible to remove RFIDs from EEPROM if their previously granted access is revoked.
    I do not use your EEPROM.getAddress, I do my own memory address maintenance because I want
    to reuse memory space freed up from deleted RFIDs.
    My question is: if I put “setMaxAllowedWrites” into the loop() does the maximum writes refer
    to each single loop run? In this case it would protect the EEPROM from being written too many
    times in one single loop() but it would allow to write to the EEPROM an infinite number of times
    (till EEPROM damaged) by rerunning the loop(); right?
    This would allow my sketch to add and delete RFIDs to the EEPROM until the EEPROM is damaged.
    Or shout I use these functions only for debugging?

    • 1) Only once, in the setup
      2) Indeed, the SetMaxAllowedWrites is a global value that sets the number of writes anywhere. It is compared to a counter that starts running when your sketch starts running. Setting it multiple times will not reset the counter (which is actually not a bad idea!). It was meant only for the debugging stage of your program.

  28. Hi,

    Is it possible to set max allowed writes to unlimited?

  29. Hi,

    thanks for coding EEPROMex and EEPROMvar! They should be included in the standard library of Arduino.

    I wanted to use a global variable of type EEPROMvar, but could not figure out a way to call setMemBase to change the base address. That is why I have modified EEPROMvar.h to also define an empty constructor. With this addition, it it possible to declare global variables, call setMemBase, and then instantiate the variable. (Actually the declaration is an instantiation, but because Arduino does not want to use new, a pure declaration of a class object is not possible).

    Below is the modified version of EEPROMvar.h and also an example. Please let me know if there is an easier way to get global variables of type EEPROMvar.

    Maybe the modification find their way into future releases of the EEPROMvar library.

    Modified version of EEPROMvar:
    [code]
    #include

    /*
    EEPROMvar.h - EEPROM variable library
    Copyright (c) 2012 Thijs Elenbaas. All right reserved.

    based on class by AlphaBeta

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    */

    #define _EEPROMVAR_DEBUG // Enables logging of not-instantiated error

    template class EEPROMVar
    {
    public:
    EEPROMVar(T init) {
    address = EEPROM.getAddress(sizeof(T));
    var = init;
    }

    // Define an empty constructor so that it is possible to declare variables
    // but without actually instantiating an object (sort of). Required when global
    // variables of type EEPROMvar are desired, but setMemPool should be called
    // prior to instantiation. Without this empty constructor, only pointers can be
    // used, which is sort of not so nice style.
    EEPROMVar() {
    address = -1;
    var = 0;
    }

    operator T () {
    return var;
    }
    EEPROMVar &operator=(T val) {
    var = val;
    return *this;
    }

    void operator+=(T val) {
    var += T(val);
    }
    void operator-=(T val) {
    var -= T(val);
    }
    void operator++(int) {
    var += T(1);
    }
    void operator--(int) {
    var -= T(1);
    }
    void operator++() {
    var += T(1);
    }
    void operator--() {
    var -= T(1);
    }
    template
    void operator /= (V divisor) {
    var = var / divisor;
    }
    template
    void operator *= (V multiplicator) {
    var = var * multiplicator;
    }
    void save(){

    // check if variable was instantiated (by calling EEPROMvar<T::EEPROMvar(T)
    // or simply declared (by calling EEPROMvar::EEPROMvar()
    #ifdef _EEPROMVAR_DEBUG
    if (address == -1) {
    Serial.println("Attempt to access EEPROMvar variable without calling EEPROMvar::EEPROMvar(T)");
    }
    #endif

    EEPROM.writeBlock(address, var);
    }

    void update(){

    // check if variable was instantiated (by calling EEPROMvar<T::EEPROMvar(T)
    // or simply declared (by calling EEPROMvar::EEPROMvar()
    #ifdef _EEPROMVAR_DEBUG
    if (address == -1) {
    Serial.println("Attempt to access EEPROMvar variable without calling EEPROMvar::EEPROMvar(T)");
    }
    #endif

    EEPROM.updateBlock(address, var);
    }

    int getAddress(){
    return address;
    }

    void restore(){

    // check if variable was instantiated (by calling EEPROMvar<T::EEPROMvar(T)
    // or simply declared (by calling EEPROMvar::EEPROMvar()
    #ifdef _EEPROMVAR_DEBUG
    if (address == -1) {
    Serial.println("Attempt to access EEPROMvar variable without calling EEPROMvar::EEPROMvar(T)");
    }
    #endif

    EEPROM.readBlock(address, var);
    }
    protected:
    T var;
    int address;
    };
    [/code]

    An example for the Arduino Uno:
    [code]
    #include
    #include

    #define CONFIG_VERSION 1 // Version number of the settings block, must not be 0

    // Declare global variables. In my humble opinion Arduino implements handling of classes
    // in a non-intuitive way, because the below lines actually call the constructor of the class,
    // while I would expect that only spcae for a variable is reserved that will later become the
    // object when instantiated. It would be nice if Arduino would force to use the new keyword
    // then the declaration below would not call the constructor.
    EEPROMVar ConfigVersion;
    EEPROMVar Counter;

    // when the EEPROMvar.h is not modified, this approach could be used, but then one has to use
    // the -> operator instead of . when accessing the methods, e.g. pCounter->getAddress()
    //EEPROMVar* pConfigVersion;
    //EEPROMVar* pCounter;

    void setup() {

    Serial.begin(19200); // set Baud rate
    Serial.println();
    Serial.println("--- testing EEPROMvar");

    // enable an upper limit of writes during developmant
    EEPROM.setMaxAllowedWrites(100);

    // set address range
    EEPROM.setMemPool(100, EEPROMSizeUno);

    // load configuration from EEPROM
    LoadConfig();
    }

    void LoadConfig() {

    // actual instantiation of the global variables
    // also defines the default values when the CONFIG_VERSION is changed
    // This (to me) looks also strange, because the new keyword is missing. This non-standard
    // way of using the C++ language teaches people the wrong thinking and should be corrected.
    ConfigVersion = EEPROMVar(0);
    Counter = EEPROMVar(0);
    //pConfigVersion = new EEPROMVar(0);

    // get config version from EEPROM
    ConfigVersion.restore();
    Serial.print("defined ConfigVersion= ");Serial.println(CONFIG_VERSION);
    Serial.print("restored ConfigVersion= ");Serial.print(ConfigVersion);
    Serial.print(" @ address "); Serial.println(ConfigVersion.getAddress());
    //Serial.print("restored pConfigVersion= ");Serial.print(*pConfigVersion);
    //Serial.print(" @ address "); Serial.println(pConfigVersion->getAddress());

    // check if it's up to date
    if (ConfigVersion == CONFIG_VERSION) {
    // yes, so restore the values
    Counter.restore();
    Serial.println("config loaded");
    } else {
    // no, so write them to EEPROM (default values are set above in the instantiation)
    Counter.save();

    ConfigVersion = CONFIG_VERSION;
    ConfigVersion.save();

    // Interestingly, this never shows up in Terminal unless I add a delay in Setup(),
    // yet the variables are updated in the EEPROM. When uploading to the arduino,
    // for some reason the setup() get's called twice.
    Serial.println("wrinting default config");
    }

    Serial.print("Counter= "); Serial.println(Counter);

    Serial.println("will add 1 to the counter and save changes to EEPROM.");
    Counter++;
    Counter.update();

    }

    void loop() {
    }
    [/code]

  30. Werkt zonder problemen en zeer gemakkelijk in gebruik! Een heel dikke merci, net wat ik nodig had!

  31. Hi Thijs, great library!
    It worked flawlessly for me on an Arduino Uno. However, I tried to initialize EEPROM data on a Sparkfun Pro Micro 5V (ATMega 32U4) and I think that caused it to brick (but I’m not entirely sure what the issue is, to be honest).
    Are you aware of any incompatibilities between EEPROMex and the ATMega 32U4?
    Thanks!

  32. Hi Thijs! Love the library, it worked flawlessly on my Arduino Uno!
    However, I bricked by Sparkfun Pro Micro 5V (running ATmega 32U4 processor) and I think, although I am not 100% sure, that the cause was the use of this library.

    I’ve managed to reset the bootloader and un-brick the board, but I am wary of trying again. Are you aware of any incompatibility between EEPROMex and ATmega 32U4 chips?

    Thanks!

    • Hi Dave,

      Really!?! I’m sorry to hear this. I had not heard of something similar before. I searched on the internet, but I could not find cases of bricked ATmega chips, by EEPROM access. The library was specifically designed to prevent the one way I know how to ruin the EEPROM, and that is by writing > 100.000 times per cell. This is not to say that you are wrong.

      Do you have a way of pinpointing the issue? That is, separate the EEPROM code from the main program and see if the EEPROM code alone bricks it, or inversely, the other code does not brick it?

      Good luck! Thijs

      • Hi Thijs – thanks for your response, and I’m sorry to have wasted your time! After getting up my courage, I ran my sketch to initialize EEPROM addresses, and it worked just fine. So your library is definitely not the culprit!

        Likely the simplest explanation is that I goofed up and uploaded code to the Pro Micro using an incorrect device selection, even though typically I was very careful to select the right one as that is a known cause of bricking.

        Thanks again, both for your response and the great library!

  33. This is a sample of my code, just the eeprom write and read event handler is included.
    I am not getting a read or write indication on the LED Displays.
    Running the Arduino Uno with a 4D LCD display (uLCD-43-PT.

    #include
    #include
    #include

    int mem_write=0;
    int mem_read=0;
    int address=0;

    //I skipped the in-between code.
    //In my Event Handler, this follows;
    //The long variable that I am trying to store in EEPROM is as an example, freq=7123456

    //————————–Memory Write————————————–
    if (Event.reportObject.object == GENIE_OBJ_WINBUTTON)
    {
    if (Event.reportObject.index == 4)
    {
    mem_write=genieGetEventData(&Event);

    if (mem_write==1) //WINBUTTON 4 has been pressed
    {
    address=0; //The Write Button, Index=4
    EEPROM.writeLong(address, freq);
    }
    }
    }

    //————————–Memory Read————————————–
    if (Event.reportObject.object == GENIE_OBJ_WINBUTTON)
    {
    if (Event.reportObject.index == 6) //The Read Button, Index=6
    {
    mem_read=genieGetEventData(&Event);
    if (mem_read==1) //WINDBUTTON 6 has been pressed {
    address=0;
    freq=EEPROM.readLong(address);
    freq_low=(freq%10000L); //Display long freq from EEPROM
    freq_high=(freq-freq_low)/10000L; //Must break freq into two parts, 2 x 4 digit LED Displays
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0×01, freq_high);
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0×00, freq_low);
    }
    }
    }

    • Hi Lee,

      I must admit I do not immediately see the issue. Have you tried storing the frequency in just in memory, to see if the EEPROM calls are the actual issue? If this works, I would push the frequency values to the terminal, to see if they give expected values

      Good luck!

Leave a Reply

Required fields are marked *.