As far as I understand it, the trick is in keeping a status for each EEPROM cell that you write, not a counter that indicates where to write.
In the below, the status for each cell is stored in the beginning of the EEPROM (from address 000); demonstrated for an improvent of 7 times (100,000 -> 700,000 saves of a single byte variable). Addresses and cell values in below are in hexadecimal.
An erased EEPROM contains 0xFF in every cell; depicted in 2nd column labeled E. The first column shows the addresses (0x000 .. 0x006 for status bytes and 0x100 .. 0x106 for the saved variable). The consecutive columns show the content of the cells after a write.
In the example below, we save a single byte (not an int, I wrote all this before the last reply) variable in EEPROM at different locations. We will save the values 0x11, 0x22, .. 0x88.
addr E 1 2 3 4 5 6 7 8 9
+------+----+----+----+----+----+----+----+----+----+----+
0 | 000 | FF | 00 | 00 | 00 | 00 | 00 | 00 | 00 | FF | 00 |
+------+----+----+----+----+----+----+----+----+----+----+
1 | 001 | FF | FF | 00 | 00 | 00 | 00 | 00 | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
2 | 002 | FF | FF | FF | 00 | 00 | 00 | 00 | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
3 | 003 | FF | FF | FF | FF | 00 | 00 | 00 | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
4 | 004 | FF | FF | FF | FF | FF | 00 | 00 | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
5 | 005 | FF | FF | FF | FF | FF | FF | 00 | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
6 | 006 | FF | FF | FF | FF | FF | FF | FF | 00 | FF | FF |
+------+----+----+----+----+----+----+----+----+----+----+
...
...
+------+----+----+----+----+----+----+----+----+----+----+
0 | 100 | FF | 11 | 11 | 11 | 11 | 11 | 11 | 11 | 11 | 88 |
+------+----+----+----+----+----+----+----+----+----+----+
1 | 101 | FF | FF | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 |
+------+----+----+----+----+----+----+----+----+----+----+
2 | 102 | FF | FF | FF | 33 | 33 | 33 | 33 | 33 | 33 | 33 |
+------+----+----+----+----+----+----+----+----+----+----+
3 | 103 | FF | FF | FF | FF | 44 | 44 | 44 | 44 | 44 | 44 |
+------+----+----+----+----+----+----+----+----+----+----+
4 | 104 | FF | FF | FF | FF | FF | 55 | 55 | 55 | 55 | 55 |
+------+----+----+----+----+----+----+----+----+----+----+
5 | 105 | FF | FF | FF | FF | FF | FF | 66 | 66 | 66 | 66 |
+------+----+----+----+----+----+----+----+----+----+----+
6 | 106 | FF | FF | FF | FF | FF | FF | FF | 77 | 77 | 77 |
Saving
When you want to save your byte variable, you loop through the status bytes till you find one that is 0xFF. For the first write, that will be at address 0x000 indicating that you have to write at address 0x100; after writing to address 0x100, you set the status byte to 0x00 indicating that you have used the cell. This is shown in column labeled 1.
When you need to save again, you again loop through the status bytes till you find 0xFF, this time at address 0x001 indicating that you have to write at address 0x101; after writing to address 0x001, you set the status byte to 0x00 indicating that you have used the cell. in the example, you can repeat this 7 times. After that the EEPROM cell values look like the column labeled 7; the last value written was 0x77 (address 0x107) and all status bytes are 0x00.
If you want to save again, you will not find a status byte with 0xFF and you clear all status bytes (0xFF) as shown in column labeled 8. Next you look again for the first status byte with value 0xFF. This will be at address 0x001 so you have to save the byte at address 0x100 and set the status byte to 0x00 after that. And so on. Be aware that you can not clear the status bytes earlier as you will not be able to find the last save value back.
Reading
Finding the last saved byte is done by reading the status bytes and finding the last 0x00. E.g. after the third save (column labled 3), address 0x002 contains the last status byte that is 0x00; this status byte is associated with address 0x102, so reading that will give you the last saved value (0x33). And after 8 writes, the last status byte that is 0x00 is at address 0x000, associated with address 0x100 which will give you 0x88.
I wrote some code to be part of this reply to demonstrate the above (and for my understanding).
#include <EEPROM.h>
const int statusByteOffset = 0x000; // status bytes for eeprom wear leveling at from address 0 onwards
const int dataByteOffset = 0x100; // databytes for eeprom wear leveling from address 256 onwards
const uint8_t levelFactor = 7;
void setup()
{
Serial.begin(57600);
}
void loop()
{
// variable to save
static uint8_t myVariable = 0;
Serial.print("Saving value ");
if (myVariable < 0x10) Serial.print(0);
Serial.println(myVariable, HEX);
// write the data
WriteEEPROM(myVariable);
myVariable += 0x11;
// read back what was saved
uint8_t tmp;
ReadEEPROM(tmp);
Serial.print("Reading back saved value -> ");
if (tmp < 0x10) Serial.print(0);
Serial.println(tmp, HEX);
// optional, show relevant EEPROM contents
ShowEEPROM();
Serial.println("");
Serial.println("");
delay(10000);
}
/*
Show EEPROM status cells and data cells
*/
void ShowEEPROM()
{
// temp var for reading EEPROM cells
uint8_t tmp;
Serial.println("++++++++++++++++++");
// loop through status and data
for (uint8_t cnt = 0; cnt < levelFactor; cnt++)
{
Serial.print("status["); Serial.print(cnt);
Serial.print("]: ");
EEPROM.get(statusByteOffset + cnt, tmp);
if (tmp < 0x10) Serial.print(0);
Serial.print(tmp, HEX);
Serial.print(" -> value["); Serial.print(cnt);
Serial.print("]: ");
EEPROM.get(dataByteOffset + cnt, tmp);
if (tmp < 0x10) Serial.print(0);
Serial.println(tmp, HEX);
}
}
/*
Write byte to EEPROM
In:
value to write
*/
void WriteEEPROM(uint8_t value)
{
// find the cell to write
int offset = -1;
for (uint8_t cnt = 0; cnt < levelFactor; cnt++)
{
uint8_t status;
EEPROM.get(statusByteOffset + cnt, status);
if (status == 0xFF)
{
offset = cnt;
break;
}
}
Serial.print("offset = "); Serial.println(offset);
// if no cells available
if (offset == -1)
{
// clear the status
for (uint8_t cnt = 0; cnt < levelFactor; cnt++)
{
uint8_t status = 0xFF;
EEPROM.put(statusByteOffset + cnt, status);
}
offset = 0;
}
Serial.print("Writing at offset "); Serial.println(offset);
// save the data
EEPROM.put(dataByteOffset + (offset * sizeof(value)), value);
EEPROM.put(statusByteOffset + offset, (uint8_t)0x00);
}
/*
Read the last saved byte
Out:
last saved value
*/
void ReadEEPROM(uint8_t &lastSaved)
{
uint8_t status;
// find the last written cell
int offset = -1;
for (uint8_t cnt = 0; cnt < levelFactor; cnt++)
{
EEPROM.get(statusByteOffset + cnt, status);
if (status == 0)
{
offset = cnt;
}
else
{
break;
}
}
// read the data
EEPROM.get(dataByteOffset + offset, lastSaved);
}
and something to clear the EEPROM with 0xFF.
#include <EEPROM.h>
void setup()
{
// initialize the LED pin as an output.
pinMode(13, OUTPUT);
for (int i = 0 ; i < EEPROM.length() ; i++)
{
EEPROM.write(i, 0xFF);
}
// turn the LED on when we're done
digitalWrite(13, HIGH);
}
void loop()
{
}
Notes:
1)
It should be reasonably easy to adjust for integers, arrays and structs as I used EEPROM.put() and EEPROM.get().
2)
Instead of status bytes, you can use status bits as described in arduino - Is there a general algorithm for microcontroller EEPROM wear leveling? - Stack Overflow; the above was easier to explain.
3)
As mentioned, there is a library which will more than likely be far better; I did not look at it.