EEPROM Read on startup on Arduino

Hello everyone,

I am starting to run a project for the purpose of long-time 8 week test. The idea is to turn 2 outputs alternately via 2-line relay.
I have written a simple code for that and implemented an integer counting how many cycles has passed outputing the value on the LCD (simple TM1637).
I'd just run that test continously forever, but I am afraid that there might be some cshortages in power supply during this test and I would probably lose information about how many cycles passed at some point.
I suppose as well, that for the safety reasons I'd need to start again this test every morning when I am at work and stop it after it.

The solution for that I came up with was to write this licznik_cykli integer each time after the cycle passed into the EEPROM memory. Unfortunately it would be 16-bit integer, not a short one, so there is also case of Write/ReadLong.

The question is, how to write the code so it would read the EEPROM at the start (with t=0 values on bytes of EEPROM being 0) and how to write the licznik_cykli value to the EEPROM at the end of the loop "loop" as it is an integer. I found the example of it on: playground.arduino.cc/Code/EEPROMReadWriteLong but I am not sure if I read it the right way.

#define DIO 34
TM1637Display display(CLK, DIO);

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(52, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(30, INPUT);
  display.setBrightness(0x0f);
}

// the loop function runs over and over again forever
void loop() {
  int nx = 0;
  int licznik_cykli = 0;
  int i;
  int start = 0;
  for (; licznik_cykli != 211;) { //docelowo 211 cykli dziennie
    display.showNumberDec(licznik_cykli,true, 4, 0);
    while (nx < 4) {
      digitalWrite(22, HIGH);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(4000);
      digitalWrite(LED_BUILTIN, LOW);
      digitalWrite(22, LOW);
      delay(12000);
      ++nx;
    }
    while (nx == 4) {
      digitalWrite(22, LOW);
      digitalWrite(24, HIGH);
      digitalWrite(52, HIGH);
      delay(12000);
      digitalWrite(24, LOW);
      digitalWrite(52, LOW);
      ++nx;
    }
    while (nx > 4) {
      delay(36000);
      ++licznik_cykli;
      nx = 0;
    }
  }
}

Quick add:
For the purpose of this project I can first flash the whole EEPROM memory with:

for (int i = 0 ; i < EEPROM.length() ; i++) {
    EEPROM.write(i, 0);
  }

and then load the rest of the code, but the rest of the questions remains the same.

pylaczynski:
I found the example of it on: playground.arduino.cc/Code/EEPROMReadWriteLong but I am not sure if I read it the right way.

There's no need for that anymore if you're using any recent version of the Arduino IDE. The EEPROM library has been updated since then to make it easy to read or write any type. See

https://www.arduino.cc/en/Reference/EEPROMGet

File > Examples > EEPROM > eeprom_get

https://www.arduino.cc/en/Reference/EEPROMPut

File > Examples > EEPROM > eeprom_put

The thing you need to remember about EEPROM is it's only rated for 100000 writes per cell. EEPROM.put() will only write to the EEPROM if the value you're writing is different than the value already in the EEPROM so that does reduce unnecessary writes but you still need to be careful because it is possible to exceed 100000 writes pretty fast if you're continually writing to it.

Hi,
You can use an external SPI Serial EEPROM or a two wire eeprom.

pert:
There's no need for that anymore if you're using any recent version of the Arduino IDE. The EEPROM library has been updated since then to make it easy to read or write any type. See

https://www.arduino.cc/en/Reference/EEPROMGet

File > Examples > EEPROM > eeprom_get

https://www.arduino.cc/en/Reference/EEPROMPut

File > Examples > EEPROM > eeprom_put

The thing you need to remember about EEPROM is it's only rated for 100000 writes per cell. EEPROM.put() will only write to the EEPROM if the value you're writing is different than the value already in the EEPROM so that does reduce unnecessary writes but you still need to be careful because it is possible to exceed 100000 writes pretty fast if you're continually writing to it.

Thank you very much for your answer, it is indeed very lucky for me, that I won't have to do this bitshifts for long integers.

About the later, there would be 12000 cycles in this test, so far from the limit. However, I'd consider implementing the eeprom_put command only once in couple of cycles, limiting total overwrites even further.

I wasn't necessarily saying the number of writes would be a problem in your project, only that it's something you have to consider. Sometimes code doesn't end up doing exactly what you intended. Sometimes you have to decide what the expected life of the device is. If the EEPROM will reach 100000 writes after five years that may or may not be an issue. If it is you can always consider doing wear leveling, where you spread out the writes over multiple EEPROM addresses. Wear leveling could easily turn the five years life expectancy into fifty years. By then another component will likely have failed.

As pert suggests, you may want to use a wear leveling scheme for your eeprom writes, and place the data in different register locations as it is written. By moving the data around, you can greatly extend the life of the eeprom.

Tracking where the last data is located is a complication, but because your cycle count data is monotonically increasing, the data becomes its own index to the location, and there is a simple wear leveling routine

Your cycle count data is an int, and you write it to 2 bytes in the eeprom. With each write, you will move this storage chunk progressively around in the eeprom to 512 possible locations.

There will be a data value in eeprom bytes 0:1, then the next at bytes 2:3, then the next in 4:5 etc. When you get to the end of the eeprom you start around again. When the power is on and the program is running the management of the next write location is straight forward.

When power is cycled, the program needs to find where the last written chunk was located, maybe use that value for something, and to start writing again from that location.

The way to do this is to start reading the eeprom from address 0 in 2 byte chunks and test each value against the next. When the value at an address is not bigger than the value in the previous address (remember to be incrementing addresses by 2), that is where to write the next value. You will need to start off with the eeprom at all 0's so that the comparison of each stored value to the next will work on the first pass. If you find that all 512 locations contain increasing data values, then your next write will be at address 0.

Your first task indeveloping this is to write your data 512 times, at sequential locations 2 bytes apart. Then practice reading what you have stored.

When you are comfortable writing the data to sequential locations and sequentially reading it out, you can move on to comparing each pair of sequential values. Test if a value is larger than the last one, and if it isn't you have found where the next write should be. The testing routine should be part of startup() in your final program.

Hi,

First, your code uses 'while' in a bad way. The second and third 'while' loop are not needed. Your program will always come out of the first while loop with nx == 4, and you will only run both of the 'while' loops once, you dont even need them.

Second, if you plan to run this program for 8 weeks, the number of repetitions is ~43200 (you have a cumulative of 112 seconds delay per iteration) , so you can use an unsigned short, and use only 2 bytes for your counter. Also; your EEPROM should last as long as 43K write cycles!

There are some threads about Arduino EEPROM wear leveling, and i've also seen something on Github, but the easiest (and must likely ost fun) way is to do it yourself!

How I would do this; 1) Write the entire EEPROM with all 0xFF values (the default for Arduino EEPROM). 2) Write a non-0xFF value as a 'bookmark', find it before every write cycle. After you find the bookmark in the EEPROM, read your variable from the address right after the bookmark. 3) Increase the counter value by 1 4) Overwrite the old bookmark with 0xFF, 5) Put the new bookmark 1 byte higher in the EEPROM, 6) Write the counter value after the bookmark, etc etc. (take into account that you dont go past the end of EEPROM, but start at the beginning again).
Theoretically, you could do without the bookmark, so you should look for a non 0xFF value in EEPROM, but this would break at 0xFF for the first byte. Your choice. using an unsigned long you would never get to oxFF for the first byte, but you still use 4 byte write cycle..

You seem to run this on a Mega, which will typically have 4K of EEPROM, so splitting 4 Bytes (old bookmark, new bookmark, 2 Byte short int) over this 4K you will only use about 42 write cycles per byte fr your whole experiment.

hth

ah. seems i was in edit mode too long (and distracted by a shiny object) and someone else alerted you about shifting the value through the range of your eeprom.
now you have three recommendations how to do this :slight_smile:

Thanks you guys a lot! I tried to improve my code step by step according to your tips (not quite got to EEPROM wear leveling yet, because I am too big of a beginner in arduino, but I'd get there tomorrow hopefully).
Here is what I got for now:

#include <TM1637Display.h>
#include <EEPROM.h>
#define CLK 30
#define DIO 34

TM1637Display display(CLK, DIO);

int address = 0;
int start = 0;

unsigned short licznik_cykli;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(52, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(30, INPUT);
  display.setBrightness(0x0f);

//get the value of the licznik_cykli
 EEPROM.get(address,licznik_cykli);
}

void loop() {

  int nx = 0;
  int i;
  int przycisk_start = digitalRead(30);
  
//run on press of the button, stop on second press
  if (przycisk_start == HIGH) {
    start = !start;
  }
  
//Main loop of cycle
  if (start == 1) {
    for (; licznik_cykli != 12000;) {
      display.showNumberDec(licznik_cykli, true, 4, 0);
      while (nx < 4) {
        digitalWrite(22, HIGH);
        digitalWrite(LED_BUILTIN, HIGH);
        delay(4000);
        digitalWrite(LED_BUILTIN, LOW);
        digitalWrite(22, LOW);
        delay(12000);
        ++nx;
      }
      digitalWrite(24, HIGH);
      digitalWrite(52, HIGH);
      delay(12000);
      digitalWrite(24, LOW);
      digitalWrite(52, LOW);
      ++nx;
      delay(36000);
      ++licznik_cykli;
      nx = 0;

//put every other value of licznik_cykli on address in EEPROM
      if (licznik_cykli % 2 == 0) {
        EEPROM.put(address,licznik_cykli);
      }
    }
  }
  else {
    display.showNumberDec(licznik_cykli, true, 4, 0);
  }
}

I implemented startup of the loop on the rise over threshold (button press), and removed the unnecessary WHILEs. Would that do this way?
My first measure of limiting the number of cycles is that it only writes every other cycle to the EEPROM.