Trying to use EEPROM to save a boolean state in case of power outage

How would I go about saving the boolean state in the EEPROM in case of power outage? Or is that not how the EEPROM works?

Here's what I have for code:

#include <Adafruit_Fingerprint.h>
#include <EEPROM.h>

#if (defined(__AVR__) || defined(ESP8266)) && !defined(__AVR_ATmega2560__)
// For UNO and others without hardware serial, we must use software serial...
// pin #2 is IN from sensor (GREEN wire)
// pin #3 is OUT from arduino  (WHITE wire)
// Set up the serial port to use softwareserial..
SoftwareSerial mySerial(2, 3);

#else
// On Leonardo/M0/etc, others with hardware serial, use hardware serial!
// #0 is green wire, #1 is white
#define mySerial Serial1

#endif

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

const uint8_t posMotorPin = 8;
const uint8_t negMotorPin = 9;
bool status;

void setup()
{
  Serial.begin(9600);
  while (!Serial);  // For Yun/Leo/Micro/Zero/...
  delay(100);

  pinMode(posMotorPin,OUTPUT);
  pinMode(negMotorPin,OUTPUT);

Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  delay(5);
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
  Serial.print(F("Security level: ")); Serial.println(finger.security_level);
  Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);

  finger.getTemplateCount();

  if (finger.templateCount == 0) {
    Serial.print("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
  }
  else {
    Serial.println("Waiting for valid finger...");
      Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates");
  }
}

void loop()                     // run over and over again
{
  getFingerprintID();
  delay(50);            //don't ned to run this at full speed.
}

uint8_t getFingerprintID() {
  uint8_t p = finger.getImage();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println("No finger detected");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK converted!
  p = finger.fingerSearch();
  if (p == FINGERPRINT_OK && status == false) {
    Serial.println("Found a print match!");
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,HIGH);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
  } else if (p == FINGERPRINT_OK && status == true) {
    digitalWrite(posMotorPin,HIGH);
    digitalWrite(negMotorPin,LOW);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_NOTFOUND) {
    Serial.println("Did not find a match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;

  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  return finger.fingerID;
}

Which Arduino board are you using ?
Have you looked at and tried the EEPROM examples ?
Be aware that there is a limit on how many times that you can safely save to an EEPROM cell

1 Like

Arduino Uno board. And I've tried a couple different things, but I can't seem to figure it out. I'm a bit new to coding and got a little in over my head on this project. Maybe there's a better way than EEPROM to save, change, and recall?

You won't find a simpler way than using EEPROM. Please post your best effort sketch and describe the problems that you have with it

1 Like

A few hints for processors that have EEPROM on chip.

  1. Be sure the library you use supports update(). This only writes to EEPROM if the value is different then what is there.
  2. The EEPROM will remember for many years without power.
  3. It has a limit of about 100,000-1,000,000 writes per cell.
  4. Flash is generally rated to ~1,000-100,000 writes
  5. Reads are non destructive.
  6. Each cell wares independently.
  7. That is write about once a minute for two years.
  8. Writing to EEPROM takes time it is not instant
  9. You cannot write to it without power so when power fails it is to late.
  10. This will not refresh a RAM location you need to do it.
  11. EEPROM memory can generally be erased on a per-byte basis
  12. If doing a lot of rapid reads.writes you need to spread it over many locations or use FRAM.
  13. If it is pseudo EEPROM you need to force it to be saved maybe commit(), this actually writes the data to the external, generall FLASH in the arduino world such as many of the ESP devices.

NOTE: If the processor does not contain internal EEPROM and supposedly does it is supported by an external device. Generally that is emulated in RAM and written back to the EEPROM/FLASH when you use the commit statement. This will depend on the library etc that you are using. Here is a link that will explain what I am trying to say: EEPROM in Arduino and ESP (ESP8266 and ESP32)

Make sure you have a diode protected capacitor or similar to hold +Vcc up long enough to support the write cycle.

Use an ADC pin to measure the voltage BEFORE the diode and capacitor.
If the nominal voltage drops below your required threshold, jump to a function that ‘saves’ your status values as quickly as possible.

Try to avoid any delay/blocking code, so the response and action is as fast as possible,

Here's my best shot. I think the problem is the new bool value isn't being stored in the EEPROM or being recalled. I'm not 100% sure.

#include <Adafruit_Fingerprint.h>
#include <EEPROM.h>

#if (defined(__AVR__) || defined(ESP8266)) && !defined(__AVR_ATmega2560__)
// For UNO and others without hardware serial, we must use software serial...
// pin #2 is IN from sensor (GREEN wire)
// pin #3 is OUT from arduino  (WHITE wire)
// Set up the serial port to use softwareserial..
SoftwareSerial mySerial(2, 3);

#else
// On Leonardo/M0/etc, others with hardware serial, use hardware serial!
// #0 is green wire, #1 is white
#define mySerial Serial1

#endif

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

const uint8_t posMotorPin = 8;
const uint8_t negMotorPin = 9;
bool status = 0;

void setup()
{
  Serial.begin(9600);
  while (!Serial);  // For Yun/Leo/Micro/Zero/...
  delay(100);

  pinMode(posMotorPin,OUTPUT);
  pinMode(negMotorPin,OUTPUT);

Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  delay(5);
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
  Serial.print(F("Security level: ")); Serial.println(finger.security_level);
  Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);

  finger.getTemplateCount();

  if (finger.templateCount == 0) {
    Serial.print("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
  }
  else {
    Serial.println("Waiting for valid finger...");
      Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates");
  }
}

void loop()                     // run over and over again
{
  getFingerprintID();
  delay(50);            //don't ned to run this at full speed.
}

int val = analogRead(0)/4;

uint8_t getFingerprintID() {
  uint8_t p = finger.getImage();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println("No finger detected");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK converted!
  p = finger.fingerSearch();
  if (p == FINGERPRINT_OK && status == false) {
    Serial.println("Found a print match!");
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,HIGH);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
  } else if (p == FINGERPRINT_OK && status == true) {
    digitalWrite(posMotorPin,HIGH);
    digitalWrite(negMotorPin,LOW);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_NOTFOUND) {
    Serial.println("Did not find a match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }
  
  EEPROM.update(status,val);

status = status + 1;
if (status == EEPROM.length()) {
  status = 0;
}
delay(100);
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;

  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  return finger.fingerID;
}

You are saving an int variable named val in a location named status

   EEPROM.update(status, val);

    status = status + 1;
    if (status == EEPROM.length())
    {
        status = 0;
    }

but you only increment the address by 1 after saving even though an int uses 2 bytes. At the very least you are overwriting one of the bytes of the int next time you save a value

Why do you keep updating the address in the first place ?

This works but isn't it going to wear out the cell fairly quick writing to it every time the code is ran? Is there a better way or is this it?

#include <Adafruit_Fingerprint.h>
#include <EEPROM.h>

#if (defined(__AVR__) || defined(ESP8266)) && !defined(__AVR_ATmega2560__)

SoftwareSerial mySerial(2, 3);

#else

#define mySerial Serial1

#endif

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

const uint8_t posMotorPin = 8;
const uint8_t negMotorPin = 9;

bool status;
bool lastStatus;

long lastDebounceTime = 0;
long debouncetime = 50;

void setup()
{
  Serial.begin(9600);
  while (!Serial);  // For Yun/Leo/Micro/Zero/...
  delay(100);

  pinMode(posMotorPin,OUTPUT);
  pinMode(negMotorPin,OUTPUT);

  status = EEPROM.read(0);
  EEPROM.write(0,false);

Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  delay(5);
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  Serial.println(F("Reading sensor parameters"));
  finger.getParameters();
  Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
  Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
  Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
  Serial.print(F("Security level: ")); Serial.println(finger.security_level);
  Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
  Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
  Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);

  finger.getTemplateCount();

  if (finger.templateCount == 0) {
    Serial.print("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
  }
  else {
    Serial.println("Waiting for valid finger...");
      Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates");
  }
}

void loop()                     // run over and over again
{
  getFingerprintID();
  delay(50);            //don't ned to run this at full speed.
}

uint8_t getFingerprintID() {
  uint8_t p = finger.getImage();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println("No finger detected");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK converted!
  p = finger.fingerSearch();
  if (p == FINGERPRINT_OK && status == false) {
    Serial.println("Found a print match!");
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,HIGH);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
    EEPROM.write(0,status);
    }
   else if (p == FINGERPRINT_OK && status == true) {
    digitalWrite(posMotorPin,HIGH);
    digitalWrite(negMotorPin,LOW);
    delay(8000);
    digitalWrite(posMotorPin,LOW);
    digitalWrite(negMotorPin,LOW);
    status = !status;
    EEPROM.write(0,status);
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_NOTFOUND) {
    Serial.println("Did not find a match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;

  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID);
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  return finger.fingerID;
}

For a single byte variable (your bool is one), you can use update() to write it and read() to read it. Update() checks if the value that you want to save has changed and only writes to EEPROM if it has changed.

For multi-byte variables, use put() and get(); see https://docs.arduino.cc/learn/built-in-libraries/eeprom. put() uses update under the hood.

1 Like

Yes it will. Stop doing that.

You want to write 1 record to eeprom only when the power is removed? You need to sample the input power using 2 resistors as a voltage divider. Then trigger a pin change interrupt when power is loss while holding the 5V up with a capacitor long enough to write the data.

This is difficult to do if you are using USB power. It can be done using a power adapter and isolate the USB power.

The sketch below will save a few bytes to EEPROM on power loss using the analog comparator interrupt.  This approach relieves loop() of having to monitor the voltage input.

/*
    Analog comparator demo. Bandgap reference and AIN1 on D7
    Board = UNO

    Comparator settings based on Nick Gammon's page:

    http://www.gammon.com.au/forum/?id=11916

    Arduino forum page:
    https://forum.arduino.cc/t/atmega328p-analog-input-comparator-using/1036684

  The first time the program is run stateCount will be 255 (out of range)
  (assuming no prior use of the EEPROM). When the button is pressed
  stateCount will cycle through three values, 0-2. Zero = OFF, one = slow
  flash, two = fast flash. When power is lost stateCount is saved to
  EEPROM in the ANALOG_COMP_vect interrupt service routine. When
  power returns stateCount will be restored from EEPROM and the code
  will resume the previously set flash mode without user input.

  Code in setup() initializes the analog comparator to generate a
  rising edge interrupt when the voltage on AIN1 - connected to a
  voltage divider* - drops below the internal 1.1v bandgap reference
  (connected to the analog comparator positive input). The voltage
  divider is connected between Vcc and GND and the wiper to AIN1 (pin 7).

  This version saves only one 8-bit variable, stateCount.

   * Current voltage divider values on pot - ~6800 and ~3300 ohms. With
   Vcc at ~5V this provides 1.634v at divider tap.

   Tested functional with fixed resistors 6.8k and 3.0k, which provides
   1.32 volts at AIN1.
*/
#include <EEPROM.h>

const byte switchPin = 11;
const byte LEDpin = 6; // This pin is used intentionally to demonstrate that
//                        pin 6 can be used for normal digital I/O when the
//                        analog comparator input AIN0 is connected to the
//                        bandgpap reference.

const byte ACOoutput = 12; // Shows state of analog comparator ACO status

const uint16_t EEPROM_BASE = 11; // non-volatile address to store/retrieve data

uint8_t stateCount = 0; // flash or don't flash LED

ISR (ANALOG_COMP_vect)
{
  /*
    When board power is lost the analog comparator triggers
    an interrupt which saves the current stateCount.
    This state will be restored when power is reapplied.
  */
  EEPROM.put(EEPROM_BASE, stateCount);
  while (1); // stay here until power down
}

void setup ()
{
  Serial.begin(115200);
  // Prepare the analog comparator for the current setup:
  // Voltage divider on pin 7, LED flasher on pin 6.

  DIDR1 = AIN1D | AIN0D; // datasheet 27.3.2 Digital Input Disable Register 1
  ACSR =  bit (ACI) ;    // (Clear) Analog Comparator Interrupt Flag
  ACSR |= bit (ACIE);    // Analog Comparator Interrupt Enable ON
  ACSR |= bit (ACBG);    // Internal bandgap reference ON
  ADCSRB = 0;            // (Disable) ACME: Analog Comparator Multiplexer Enable

  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on rising edge)
  ACSR |= bit (ACIS0);
  ACSR |= bit (ACIS1);

  pinMode(LEDpin, OUTPUT); // Use the now disconnected PD6 as an output
  pinMode(ACOoutput, OUTPUT);

  pinMode(switchPin, INPUT_PULLUP); // with parallel .1uf capacitor debouncing

  EEPROM.get(EEPROM_BASE, stateCount); // Restore stateCount from EEPROM
  //  Serial.println("end of setup");
  //
  // lamp test
  //
  digitalWrite(LEDpin, HIGH);
  digitalWrite(ACOoutput, HIGH);
  delay(1500);
  digitalWrite(LEDpin, LOW);
  digitalWrite(ACOoutput, LOW);
}  // end of setup


void loop ()
{
  static byte lastSW;
  byte switchState = digitalRead(switchPin);

  bool comparatorOutput = ACSR & bit(ACO); // state of analog comparator
  digitalWrite(ACOoutput, comparatorOutput);
  Serial.println(comparatorOutput);

  if (switchState == LOW && lastSW == HIGH) { // count on switch pressed
    stateCount++;
    if (stateCount > 2) stateCount = 0;
  }
  lastSW = switchState;

  if (stateCount == 1) { // LED flashes slow
    delay(1000);
    digitalWrite(LEDpin, HIGH);
    delay(1000);
    digitalWrite(LEDpin, LOW);
  }
  else  if (stateCount == 2) { // LED flashes fast
    delay(500);
    digitalWrite(LEDpin, HIGH);
    delay(500);
    digitalWrite(LEDpin, LOW);
  }
  else { // LED is OFF
    digitalWrite(LEDpin, LOW);
  }
}

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.