diff --git a/README.md b/README.md index f188e95..64c2b44 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/examples/deep_sleep_boot_count/deep_sleep_boot_count.ino b/examples/deep_sleep_boot_count/deep_sleep_boot_count.ino index c0bef11..2564136 100644 --- a/examples/deep_sleep_boot_count/deep_sleep_boot_count.ino +++ b/examples/deep_sleep_boot_count/deep_sleep_boot_count.ino @@ -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 @@ -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(); diff --git a/library.properties b/library.properties index 4bbfc67..988e7e1 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=ESP32_RTC_EEPROM -version=0.1.0 +version=0.2.0 author=Rop Gonggrijp maintainer=Rop Gonggrijp 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 diff --git a/src/ESP32_RTC_EEPROM.cpp b/src/ESP32_RTC_EEPROM.cpp index 1f2f5dc..cf23940 100644 --- a/src/ESP32_RTC_EEPROM.cpp +++ b/src/ESP32_RTC_EEPROM.cpp @@ -22,12 +22,16 @@ */ #include "ESP32_RTC_EEPROM.h" +#include +#include #include -// 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) { } @@ -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; } diff --git a/src/ESP32_RTC_EEPROM.h b/src/ESP32_RTC_EEPROM.h index 2102069..0afc4aa 100644 --- a/src/ESP32_RTC_EEPROM.h +++ b/src/ESP32_RTC_EEPROM.h @@ -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 class EEPROMClass { @@ -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();