Skip to content

Commit

Permalink
Added toNVS() and fromNVS(), version to 0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ropg committed Mar 4, 2024
1 parent b8e3918 commit 1291f05
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 10 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ If your application keeps the ESP32 battery-powered but puts it in deep sleep, i

This library will emulate EEPROM in RTC RAM, which stays powered during deep sleep. To learn more about deep sleep, check [this easy tutorial](https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/), or [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32s3/api-reference/system/sleep_modes.html) if you want all the details. Like mentioned above, use this library only if your existing code uses EEPROM. Simply having some variables survive deep sleep is way easier, check the tutorial.

### Optional saving to NVS (flash)

If you call `EEPROM.toNVS()`, a copy of your EEPROM data will be save to the key "eeprom" in NVS flash, exactly like when you would use the original `EEPROM.h`. Whenever the ESP32_RTC_EEPROM wakes up with an empty RTC RAM (which is detects by the presence of a magic word), it will try to see if there's a saved copy of the right size. If so, that is loaded. You can also manually revert to the last save by calling `EEPROM.fromNVS()`.

### Usage

1. Add this library using the library manager or by cloning this repository into your Arduino library folder.
Expand Down
20 changes: 17 additions & 3 deletions examples/deep_sleep_boot_count/deep_sleep_boot_count.ino
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* This will show you the boot count, but only if you use the button to wake up from
from deep sleep. As you will see, hitting the reset button, reflashing and powering
down will all start the count at zero again.
down will all start the count at the last multiple of 10, because that is the last
time the contents of the RTC RAM were copied to NVS (flash).
*/

#define BUTTON_GPIO GPIO_NUM_0
Expand All @@ -16,10 +17,23 @@ void setup() {
// data will always take up EEPROM_SIZE in RTC RAM.
EEPROM.begin(512);

// Read from "EEPROM"
uint8_t bootcount = EEPROM.read(0);

// Increase bootcount
bootcount++;

// Print boot count
Serial.println("Boot count: " + String(EEPROM.read(0)));
Serial.println("Boot count: " + String(bootcount));

// Write new value
EEPROM.write(0, EEPROM.read(0) + 1);
EEPROM.write(0, bootcount);

// Save a copy to NVS every 10 boots, so we can resume from this point if we reset / lose power
if (bootcount % 10 == 0) {
Serial.println("Backing up the current EEPROM data from RTC RAM to NVS (flash).");
EEPROM.toNVS();
}

// Data is written immediately, calls to commit() and end() are ignored
EEPROM.commit();
Expand Down
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name=ESP32_RTC_EEPROM
version=0.1.0
version=0.2.0
author=Rop Gonggrijp <rop@gonggri.jp>
maintainer=Rop Gonggrijp <rop@gonggri.jp>
sentence=EEPROM emulation that stores in RTC RAM. Survives deep sleep, but not reset or power loss.
paragraph=This library provides a drop-in replacement for the standard EEPROM library that stores data in the ESP32's RTC memory. This memory is preserved during deep sleep, but not during reset or power loss. The library is designed to be a drop-in replacement for the standard EEPROM library, so you can use it with existing code that uses EEPROM.
paragraph=This library provides a drop-in replacement for the standard EEPROM library that stores data in the ESP32's RTC memory. This memory is preserved during deep sleep, but not during reset or power loss. Optional saving and automatic loading of a backup copy to NVS (flash). Designed to be a drop-in replacement for the standard EEPROM library, so you can use it with existing code that uses EEPROM.
category=Other
url=https://github.com/ropg/ESP32_RTC_EEPROM
architectures=esp32
Expand Down
71 changes: 68 additions & 3 deletions src/ESP32_RTC_EEPROM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
*/

#include "ESP32_RTC_EEPROM.h"
#include <nvs.h>
#include <esp_partition.h>
#include <esp_log.h>

// static members
uint8_t RTC_DATA_ATTR EEPROMClass::_data[EEPROM_SIZE];
// Wake up with a magic word in there that lets us detect that we're starting from reset or power-down.
uint8_t RTC_DATA_ATTR EEPROMClass::_data[EEPROM_SIZE] = { 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42 };

size_t EEPROMClass::_size = EEPROM_SIZE;


EEPROMClass::EEPROMClass(void)
{
}
Expand All @@ -40,12 +44,73 @@ EEPROMClass::EEPROMClass(uint32_t sector)
EEPROMClass::~EEPROMClass() {
}

bool EEPROMClass::fromNVS() {
nvs_handle _handle;
char _name[] = EEPROM_FLASH_PARTITION_NAME;

esp_err_t res = nvs_open(_name, NVS_READWRITE, &_handle);
if (res != ESP_OK) {
log_e("Unable to open NVS namespace: %d", res);
return false;
}

size_t key_size = 0;
res = nvs_get_blob(_handle, _name, NULL, &key_size);
if(res != ESP_OK && res != ESP_ERR_NVS_NOT_FOUND) {
log_e("Unable to read NVS key: %d", res);
return false;
}
if (key_size != _size) {
log_e("EEPROM in NVS not same size as EEPROM in RTC RAM");
return false;
}
nvs_get_blob(_handle, _name, _data, &key_size);
return true;
}

bool EEPROMClass::toNVS() {
nvs_handle _handle;
char _name[] = EEPROM_FLASH_PARTITION_NAME;

esp_err_t res = nvs_open(_name, NVS_READWRITE, &_handle);
if (res != ESP_OK) {
log_e("Unable to open NVS namespace: %d", res);
return false;
}

res = nvs_set_blob(_handle, _name, _data, _size);
if (res != ESP_OK) {
log_e("Unable to write NVS key: %d", res);
return false;
}
res = nvs_commit(_handle);
if (res != ESP_OK) {
log_e("Unable to commit NVS key: %d", res);
return false;
}
nvs_close(_handle);
return true;
}

bool EEPROMClass::begin(size_t size) {
if (!size || size > EEPROM_SIZE) {
return false;
}
_size = size;
// _data = &_EEPROM_DATA[0];
// See if we just woke up from reset or power-off
uint64_t* magic_word = (uint64_t*) _data;
// magic_word reversed because ESP32 is little-endian
if (*magic_word == 0x4223422342234223) {
log_w("Woke up cold, trying recover RTC EEPROM contents from NVS backup.");
// if we did, see if we have a saved copy in NVS (flash)
if (!fromNVS()) {
log_w("No backup copy found, EEPROM starts with clean slate.");
// If not, delete the magic word and start with a clean slate
*magic_word = 0;
} else {
log_w("RTC EEPROM contents recovered from NVS backup.");
}
}
return true;
}

Expand Down
10 changes: 8 additions & 2 deletions src/ESP32_RTC_EEPROM.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#ifndef EEPROM_h
#define EEPROM_h
#ifndef ESP32_RTC_EEPROM_h
#define ESP32_RTC_EEPROM_h

#define EEPROM_SIZE 2048

#ifndef EEPROM_FLASH_PARTITION_NAME
#define EEPROM_FLASH_PARTITION_NAME "eeprom"
#endif

#include <Arduino.h>

class EEPROMClass {
Expand All @@ -35,6 +39,8 @@ class EEPROMClass {
~EEPROMClass(void);

static bool begin(size_t size);
static bool fromNVS();
static bool toNVS();
static uint8_t read(int address);
static void write(int address, uint8_t val);
static uint16_t length();
Expand Down

0 comments on commit 1291f05

Please sign in to comment.