expire counter for eeprom.

Is there a way or piece of code so the EEPROM can keep track of how many times it has been writing to an address?
I know that EEPROM has a write cycle around 100.000 times.
Or is there another way so the EEPROM gets least writes possible.
now in my code, I only write when a value has changed. That happens almost every hour.

and when the EEPROM "track counter" exceeds the 100.000 times.
Is it possible that address is so-called damaged of broken and shoves the value 1 address further?

Your code can keep track of the last address it wrote and increment by 1 the next time it writes. This way the 'wear' on the EEPROM is shared among all the addresses.

Also search 'arduino wear leveling'.

Macro_c:
Ahh okay, but how does the code where the value is when the Arduino gets reset?
With a for loop look for value?
But then every other address needs to be 0 or another value so the loop can see that that isn't the value it's looking for.
That means when the program needs to write a value to the next address the previous value needs to be set to 0 or another value, and that shortens the lifetime into half.

And how could I do this with an array?
Most of the values aren't changing at the same time.

Dougp:
I have done that, but I'm not at that level yet of coding.
still interesting to do with a project

now in my code, I only write when a value has changed. That happens almost every hour.

What is the data being stored? If it is monotonically increasing, then it can become its own index and the wear leveling scheme is simplified.

There is also a wear leveling libary EEPROMwearlevel.h which is available through the library manager of the IDE.

Documentation is here

cattledog:
What is the data being stored? If it is monotonically increasing, then it can become its own index and the wear leveling scheme is simplified.

The data is stored in an int array of about 10 long With numbers in it between 0 and 100.
The stored data can be changed at anytime with a potentiometer and the potentiometer is attached to a map function.

The number or address of the array for example data[2], can be completely random
Values of the array aren't stored like this: a value has changed address of the array +1.

data[3]{100,38,76};

And also Thanks for the tip about the library!

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.

The data is stored in an int array of about 10 long With numbers in it between 0 and 100.

This sounds like it is a byte (uint8_t) array. It will make a big difference if all your entries take 2 eeprom cells to hold a 16 bit integer or 1 cell to hold an 8 bit integer.

The number or address of the array for example data[2], can be completely random
Values of the array aren't stored like this: a value has changed address of the array +1.
data[3]{100,38,76};

I can not understand what you are saying here about the organization of the data.
It's not clear to me if you are referring to elements within an array, or multiple arrays.

Can you please try and explain it to me again? Perhaps you can post the code which is writing to the eeprom so we can see the address managment.

I am thinking that you have a data chunk which occupies 10 cells of the eeprom and which you want to move around in the eeprom for wear leveling.

byte myArray[10] = {xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx};

Thanks a lot for what you explained in the previous post!

but what I'm trying to do is implement wear levelling for an int array.

cattledog:
This sounds like it is a byte (uint8_t) array. It will make a big difference if all your entries take 2 EEPROM cells to hold a 16-bit integer or 1 cell to hold an 8-bit integer.

I have only one array for storing the values.
The largest value is 100 and so it's less than an 8-bit integer.
could that mean that we can store 2 values in one address?

I only have a reasonable understanding of using EEPROM.

Reading:
Getting the values from EEPROM and putting it in the int array.
I'm doing this once in setup.

int eepromSize = 10;
int parameters[eepromSize];
void setup
{
EEPROM.begin(eepromSize);
for (int p = 0; p <= eepromSize; p++) {
    parameters[p] = EEPROM.read(p);
   }
}

Writing
And this is for writing to the EEPROM. currently I have no wear leveling and I'm trying to understand it
It will only write when the value isn't the same as the value stored in eeprom.
it just writes the values stored in the array to the eeprom.

for (int p = 0; p < numOfSchermen; p++) {
      if (parameters[p] != EEPROM.read(p)) {
        EEPROM.write(p, parameters[p]);
        EEPROM.commit();
        
        }
      }
      delay(5);
    }

cattledog:
I am thinking that you have a data chunk which occupies 10 cells of the EEPROM and which you want to move around in the EEPROM for wear levelling.

byte myArray[10] = {xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx};

that's what I want to do, but I don't understand how I could do that.
What I don't understand is how I could retrieve an array that is "randomly" stored in EEPROM and how I wear level the EEPROM when using an array.

I hope that this has helped a bit.

I see this in your code.

EEPROM.begin();
EEPROM.commit();

Are you using and esp8266 or esp32?

These devices have emulated eeproms in flash memory, and there are some special issues related to wear leveling on those platforms. The flash sector used for eeprom can be moved around, and I'm unclear of whether or not there is full sector erasing whenever the sector is written to. If that's the case then the wear leveling methods used for the avr eeprom do not apply.

I know there are several special libraries for wear leveling in the esp devices.

The stored data can be changed at anytime with a potentiometer and the potentiometer is attached to a map function.

How often do you adjust the potentiometer and change the values to be stored?

cattledog:
Are you using and esp8266 or esp32?

I'm using a esp32.

cattledog:
These devices have emulated eeproms in flash memory, and there are some special issues related to wear leveling on those platforms. The flash sector used for eeprom can be moved around, and I'm unclear of whether or not there is full sector erasing whenever the sector is written to. If that's the case then the wear leveling methods used for the avr eeprom do not apply.

Ohh Okey that is something interesting that I didn't know.
But do I need a special eeprom libraries for esp32? Or can I stick with the eeprom.h?

cattledog:
How often do you adjust the potentiometer and change the values to be stored?

About 4 or 5 times times per day.
Because when the value has been changed the program saves it to eeprom

My recommendation is to try using ESP_EEPROM by j. watson. It is available through the library manager.

I'm not clear if it will work for the ESP32 as well as the ESP8266 but its worth a try. I don't have an ESP32 or the ESP32 core loaded on my computer so I haven't tried it.

This library will take your array, and store it in different areas of the flash space reserved for the eeprom. It's the equivalent of what you would do to wear level by moving the storage location in a true eeprom. There are several library examples, and it looks like you will use the standard syntax of the eeprom library you already use.

With your 4-5 changes per day, you may not need actually to worry about 100K writes, but I would give the library a try.

I suspect that the flash is limited to 10k writes; so with 10 writes a day, it would die in 3 years.

Could always add an SPI FRAM to your project. 10^14 writes, (100,000,000,000,000), you'll never wear it out.
Fast writing speed like SRAM too, not slow like EPROM/EEPROM.

cattledog:
My recommendation is to try using ESP_EEPROM by j. watson. It is available through the library manager.

Thanks for that cattledog. I will have a look at what I could use.
Have found it on GitHub, not in the library manager.
And also the 5 writes per day over 100k writes is not a lot but it will die not that fast.

sterretje:
I suspect that the flash is limited to 10k writes; so with 10 writes a day, it would die in 3 years.

and I want it to have a long life and not that short. but that's per address.

CrossRoads:
Could always add an SPI FRAM to your project. 10^14 writes, (100,000,000,000,000), you'll never wear it out.
Fast writing speed like SRAM too, not slow like EPROM/EEPROM.

that could be the solution, but then the next questions emerge:
How could I talk to that chip using SPI?
How much storage do I need and which chip?
And is it beneficial for the project that I use this?

I'm planning to make a PCB and then I could add it to the designs.
I use JLCPCB and LCSC.
The upsides:
easy to replace when damaged.
Fast write speeds (not needed in this project)

The downside:
complicates the code?
I don't have experience with this.
(but when needed i want to learn it.)

Google "fram spi library arduino", it comes up with a several hits, most seem to lead to an Adafruit library.

Will be a little more complex than EEPROM as you have to do something like

digitalWrite (csFRAM, LOW);
SPI.transfer (FRAMaddressByte1); // for addresses up to 255
SPI.transfer (FRAM addressByte2); // for addresses up to 65535
SPI.transfer (FRAM addressByte3); // for addresses up to 1,048,575 (1M)
SPI.transfer(dataByte); // for a write, or
dataByte = SPI.transfer(); // for a read
digitalWrite (csFRAM, HIGH);

The above would take something like 4 uS for the SPI transfers, and however long 2 digitalWrites take, altho they can be shortened with fastDigitalWrite, or direct port manipulation, so maybe 6-7uS for a single byte.
And likely some kind of block transfer is also supported.

How big is up to you. FRAMs come in many byte options in the small 8-pin package, some are 3.3V, some are 5V, give yourself the options to support either.
https://www.digikey.com/products/en/integrated-circuits-ics/memory/774?k=fram+memory&k=&pkeyword=fram+memory&sv=0&pv961=349252&pv2043=406106&pv1291=294926&sf=1&FV=-8|774&quantity=&ColumnSort=0&page=1&stock=1&pageSize=25

Can be up to 1M x 8 on one chip if you wanted
https://www.digikey.com/product-detail/en/cypress-semiconductor-corp/CY15B108QN-40SXI/428-4717-ND/10378296

FRAM has SRAM speed, EEPROM nonvolatility, and 10^14 write cycles, so its unlimited for all practical purposes; 10 billion days lifetime if writing 1000 times/per day. Do some math for however much you expect you expect to use it.

The downside:
complicates the code?

It does not complicate the code. You will have to write code just like you have to write code for the EEPROM approach; that might be complicated but it does not complicate the code when you put it in a function that you call when you want to save the values.

thank you all for the help.

I will see what I want and can do.

I think that Fram or something like that is cool to learn and I will do that in another version of my project.

and also I will be gone for a week or so and I hope then that I could see what I could use.
I will post an update when I have done that