EEPROM & Counting

Hi Everyone,

I’m working on a project that includes a REED switch and continues counting as its is triggered.

I also need the device to save the count in EEPROM in case the device is rebooted. What is the best approach to get this done. Keep in mind I may go past the 100K EEPROM limit.

The code I have so far:

void loop();
{
  state = digitalRead(pin);
  // compare the buttonState to its previous state
  if (state != lastButtonState) {
    // if the state has changed, increment the counter
    if (digitalRead(pin) == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      lastButtonState = 1;
      buttonPushCounter++;
      delay(250);
    } else if (digitalRead(pin) == LOW) {
      lastButtonState = 0;
    }
  }
}

if you are concerned about the EEPROM write limit consider FRAM

Get rid of the delay.

Store the value in a variable and write to EEPROM occasionally, not on every update. You need to figure out how often to save. You can also find ways to write to different areas of the EEPROM to even out the wear.

FRAM is a very good idea, not used it myself.

To help choose between a wear leveling scheme with the internal EEPROM or using external FRAM storage, it will help to know more about your data and how much you need to store.

Is the count an unsigned int(2 bytes) or an unsigned long(4 bytes)?

Assuming a 4 byte long, you have 256 different locations to store the data. Is 256 X 100K more or less than the number of EEPROM writes you need to perform.

Doe you need to store every change in count?

Because you have a monotonically increasing count stored in EEPROM, the value becomes its own index to the last number written and the routine for finding the last number after a restart is very much simplified. You just have to find where the value in a location is not larger than the previous value.

Here is some code which demonstrates a wear leveling routine for 4 byte longs.

#include <EEPROM.h>
unsigned long count;
int index;
#define lastIndex  EEPROM.length() - sizeof(count) //1020
boolean freshStart = true;
//boolean freshStart = false;

void setup() {
  Serial.begin(115200);
  if (freshStart)
    clearEeprom();

  //find last count and index in Eeprom with checkEeprom()function
  Serial.print("Checking for last stored count = ");
  index = checkEeprom();
  Serial.println(EEPROM.get(index, count));
  Serial.print("Located at EEPROM index = ");
  Serial.println(index);
}

void loop() {
  //emulate machine cycle incrementing count and index
  static boolean machineCycleComplete = false;
  static unsigned long lastMachineComplete = 0;
  if (millis() - lastMachineComplete >= 2000)
  {
    machineCycleComplete = true;
    lastMachineComplete = millis();
  }
  //values to incremented by machine cycle
  if (machineCycleComplete == true)
  {
    machineCycleComplete = false;
    count += 2;
    index += sizeof(count);//increment address by 4 with long

    if (index > lastIndex)
      index = 0;

    EEPROM.put(index, count);
    Serial.print("index = ");
    Serial.print(index);
    Serial.print("  count = ");
    Serial.println(count);
    machineCycleComplete = false;
  }
}

void clearEeprom()
{
  Serial.println("Clear Eeprom Cells");
  for (int i = 0; i < EEPROM.length(); i++)
  {
    EEPROM.write(i, 0);
  }
  Serial.println("All eeprom cells set to zero.");
  Serial.println("Initialize freshStart to false and reload program");
  while (1);//stop program 
}

int checkEeprom()
{
  static unsigned long lastValue = 0;
  static unsigned long value = 0;
  for (int i = 0; i <= lastIndex; i += sizeof(count))
  {
    lastValue = value;
    EEPROM.get(i, value);
    if (value < lastValue)//next value is not larger than last
    {
        return (i - sizeof(count));//last valid index
    }
    if (i == lastIndex)//no smaller value found or values all equal as zeros
    {
      return i;
    }
  }
}

If you don't mind an additional module, a battery backed Real Time Clock module usually has some RAM that you can use.

A module that uses an MCP7940N RTC will give you 64 bytes of battery backed RAM that you can write to as often as you like. Or, a DS1307 RTC has 56 bytes of battery backed RAM.

And the RTC (though the DS1307 is not reputed to keep time particularly well) gives you the option of logging.

I don't want to add any additional hardware.

@cattledog How would I store a 9 digit number in EEPROM?

@cattledog How would I store a 9 digit number in EEPROM?

As an unsigned long. 0 to 4,294,967,295 (2^32 - 1). Takes 4 bytes as in the example code.

https://www.arduino.cc/reference/en/language/variables/data-types/unsignedlong/

Use EEPROM.put() and EEPROM.get(). These functions can store and retrieve any data type. You will need to manage the address locations to accommodate the 4 byte size in your code.

Thanks @cattledog

Few follow up questions (sorry I'm a beginner at this):

  • My first index would be at 0 and once I reach ~100k writes I should move to index 4, 8, 12 and so on, correct? I want the ability to reset this count as well.
  • If the above is correct, I was thinking about running in parallel the overall total number of writes to eeprom and store it at index 1024 for writes between 1 - 100k, move to index 1020 for writes between 100001 - 200000, and so on. Would an approach like this make sense?

My first index would be at 0 and once I reach ~100k writes I should move to index 4, 8, 12 and so on, correct?

This method is possible, but you loose some memory space keeping track of your location and the number of writes. The method I showed constantly moves the data to a new location, and the data itself becomes an index to where it is located. It assumes you will not reach the total maximum number of writes.

If the above is correct, I was thinking about running in parallel the overall total number of writes to eeprom and store it at index 1024 for writes between 1 - 100k, move to index 1020 for writes between 100001 - 200000, and so on. Would an approach like this make sense?

I'm not sure. If you don't understand the method I demonstrated and you feel more comfortable with fixed index locations, then that can be made to work. It's certainly less optimal for total storage.

As I said, most wear leveling approaches do not use up any location and then move on. They continually move data to a new location. The complications are all about where the last valid write is stored. The monotonically increasing data makes it self indexing on restart with the search routine I showed in the example.

Thanks. This makes total sense now! Really appreciate the help!!!