My sketch uses EEPROM_writeAnything to save state data on a regular basis, however being concerned about EEPROM failure I have made a slight change to the code to check if what is being written is different from what is already stored in the EEPROM, and only writing it if it is different from what is already there.
While there has been some discussion that EEPROM.write() should have this check already built in, having it in this function at least saves a lot of worry that you may be irrecoverably wearing out the EEPROM through too-frequent write cycles.
While there is an extra step involved in reading-before-write, it takes a lot less time to read from the EEPROM than it does to write to it - therefore the net effect of this change will usually be an increase in speed of execution unless all data being written is different from what is already stored.
The revised EEPROM_writeAnything function is as follows:
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.read(ee)==*p ? e++, p++; EEPROM.write(ee++, *p++); // Only write the data if it is different to what's there
return i;
}
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.read(ee)==*p ? e++, p++; EEPROM.write(ee++, *p++); // Only write the data if it is different to what's there
return i;
}
There is a problem with your code. Did you compile it?
Your conditional operator should look more like this:
There is also no saving over using an if statement.
for (i = 0; i < sizeof(value); i++){
if( EEPROM.read(ee)==*p ){
ee++;
p++;
}else{
EEPROM.write(ee++, *p++); // Only write the data if it is different to what's there
}
}
Also for a size optimisation you should use the eeprom functions directly. When not using the EEPROM library this code is 74 bytes smaller on an Uno, probably faster too as the library functions probably aren't inlined.
for (i = 0; i < sizeof(value); i++){
const byte b = *p;
if( eeprom_read_byte( ( uint8_t* ) ee ) != b )
eeprom_write_byte( ( uint8_t* ) ee++, b ), ++p;
else
ee++, p++;
}
EDITED '==' to '!=' in code. Explained in post below.
Thanks, you are right - the missing curly brackets is showing up my rusty C++. My original code compiles fine but doesn't work as intended without them.
However I'm pretty sure your final code also has an error in it (the if statement should be !=).
for (i = 0; i < sizeof(value); i++){
const byte b = *p;
if( eeprom_read_byte( ( uint8_t* ) ee ) != b )
eeprom_write_byte( ( uint8_t* ) ee++, b ), ++p;
else
ee++, p++;
}
zylantha:
Thanks, you are right - the missing curly brackets is showing up my rusty C++. My original code compiles fine but doesn't work as intended without them.
However I'm pretty sure your final code also has an error in it (the if statement should be !=).
for (i = 0; i < sizeof(value); i++){
const byte b = p;
if( eeprom_read_byte( ( uint8_t ) ee ) != b )
eeprom_write_byte( ( uint8_t* ) ee++, b ), ++p;
else
ee++, p++;
}
Yes, alas its a product of trying to write too fast.
There was more than the curly brackets, notice the 0x00 converted to a pointer, its needed as EEPROM.write returns nothing.
Having a system that regularly writes parameters to the EEPROM can wear out the
EEPROM, since it is only guaranteed to endure 100 k erase/write cycles. Writing the
parameters to a circular buffer in EEPROM where each of the elements in the buffer
can endure 100 k erase/write cycles can circumvent this. However, if the system is
exposed to RESET conditions, such as power failures, the system needs to be able to
identify the correct position in the circular buffer again. This document describes how
to make safe high endurance parameter storage in EEPROM
There are several techniques for extending the life of EEPROM (called wear leveling). The 100K cycle limit refers to a programming and erase cycle. Programming a cell, then erasing it, counts as 1 cycle. One method of extending the life is to "program many, erase once". For example, if you had one byte that had to be changed on a regular basis, you could write this value into an EEPROM array. Each time that you had to write the value, you could find the next erased address in the array and write the value there. When the predefined array was filled, it would be erased, and the process would start over. This would count as 1 program/erase operation for the entire array. So, if you had a 16 byte EEPROM array, you could store this single byte 16 x 100K or 1.6 million times.
You can also extend life of an EEPROM address "horizontally" as well. By programming individual bits (without erasure), you can also extend life. Example:
76543210
11111111 beginning state (erased)
11111110 program
11111100 program
11111000 program
11110000 program
11100000 program
11000000 program
10000000 program
00000000 program
11111111 erase
This would also count as 1 program/erase cycle.
Programming an already programmed bit does not count towards the programming cycle count, nor does erasing an already erased cell. The majority of damage to the cell comes from current flow into or out of the cell (changing the cell state) during a program or erase cycle.
Personally, I'm just too darn lazy to worry about 100K eeprom cycle issues... If the uC in the project lives 2x or 3x the warranty period, I'm good with that.
zylantha:
Thanks, you are right - the missing curly brackets is showing up my rusty C++. My original code compiles fine but doesn't work as intended without them.
However I'm pretty sure your final code also has an error in it (the if statement should be !=).
for (i = 0; i < sizeof(value); i++){
const byte b = p;
if( eeprom_read_byte( ( uint8_t ) ee ) != b )
eeprom_write_byte( ( uint8_t* ) ee++, b ), ++p;
else
ee++, p++;
}
just a question, shouldn't the read address match the write address?
looks to me like your reading one byte before the byte that is being written?
your reading address is ee
your write address is ee++
i am not that brilliant at c++ coding its just an observation that may or may not be accurate.
so i guess what i am asking is the code above correct?
I thought about using EepromAnything as I need to write values up to 1000.
It seemed an easy way out to overcome the max of 255.
My earlier solution was to divide the value by 4, but that way reading the value is always in steps of 4, loosing the original value.
Probably need to look into the way to split the first two digits from the second two.
So:
1000 into 10 and 00
980 into 9 and 8
760 into 8 and 60
etcetera.