From 619568db5bcc219091c653a5ced5e378b3a5643b Mon Sep 17 00:00:00 2001 From: lbernstone Date: Tue, 23 Apr 2019 14:55:12 -0600 Subject: [PATCH] Converted EEPROM library to use nvs instead of partition. (#2678) * Converted EEPROM library to use nvs instead of partition. Removed eeprom partition from all partition table CSV files. * Changed variable names, added some comments, formatting as per me-no-dev's requests * Checks for memory on malloc * Moved include nvs.h from header to code * Reworked the extra example to make it more clear how to actually use the library and persist data --- libraries/EEPROM/README.md | 4 + .../examples/eeprom_class/eeprom_class.ino | 4 +- .../examples/eeprom_extra/eeprom_extra.ino | 101 +++++++++--- libraries/EEPROM/src/EEPROM.cpp | 150 +++++++++++------- libraries/EEPROM/src/EEPROM.h | 20 +-- tools/partitions/default.csv | 3 +- tools/partitions/default_16MB.csv | 3 +- tools/partitions/default_8MB.csv | 3 +- tools/partitions/default_ffat.csv | 3 +- tools/partitions/ffat.csv | 3 +- tools/partitions/huge_app.csv | 3 +- tools/partitions/large_spiffs_16MB.csv | 3 +- tools/partitions/min_spiffs.csv | 3 +- tools/partitions/minimal.csv | 3 +- tools/partitions/no_ota.csv | 3 +- tools/partitions/noota_3g.csv | 3 +- tools/partitions/noota_3gffat.csv | 3 +- tools/partitions/noota_ffat.csv | 3 +- 18 files changed, 190 insertions(+), 128 deletions(-) create mode 100644 libraries/EEPROM/README.md diff --git a/libraries/EEPROM/README.md b/libraries/EEPROM/README.md new file mode 100644 index 00000000..896ca5b3 --- /dev/null +++ b/libraries/EEPROM/README.md @@ -0,0 +1,4 @@ +## EEPROM + +EEPROM is deprecated. For new applications on ESP32, use Preferences. EEPROM is provided for backwards compatibility with existing Arduino applications. +EEPROM is implemented using a single blob within NVS, so it is a container within a container. As such, it is not going to be a high performance storage method. Preferences will directly use nvs, and store each entry as a single object therein. diff --git a/libraries/EEPROM/examples/eeprom_class/eeprom_class.ino b/libraries/EEPROM/examples/eeprom_class/eeprom_class.ino index af098809..03cfa856 100644 --- a/libraries/EEPROM/examples/eeprom_class/eeprom_class.ino +++ b/libraries/EEPROM/examples/eeprom_class/eeprom_class.ino @@ -26,8 +26,8 @@ #include "EEPROM.h" // Instantiate eeprom objects with parameter/argument names and size same as in the partition table -EEPROMClass NAMES("eeprom0", 0x1000); -EEPROMClass HEIGHT("eeprom1", 0x500); +EEPROMClass NAMES("eeprom0", 0x500); +EEPROMClass HEIGHT("eeprom1", 0x200); EEPROMClass AGE("eeprom2", 0x100); void setup() { diff --git a/libraries/EEPROM/examples/eeprom_extra/eeprom_extra.ino b/libraries/EEPROM/examples/eeprom_extra/eeprom_extra.ino index b2ea72f7..0ae4189a 100644 --- a/libraries/EEPROM/examples/eeprom_extra/eeprom_extra.ino +++ b/libraries/EEPROM/examples/eeprom_extra/eeprom_extra.ino @@ -13,78 +13,127 @@ void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println("\nTesting EEPROM Library\n"); - if (!EEPROM.begin(EEPROM.length())) { + if (!EEPROM.begin(1000)) { Serial.println("Failed to initialise EEPROM"); Serial.println("Restarting..."); delay(1000); ESP.restart(); } - int address = 0; // Same address is used through the example + int address = 0; EEPROM.writeByte(address, -128); // -2^7 - Serial.println(EEPROM.readByte(address)); + address += sizeof(byte); EEPROM.writeChar(address, 'A'); // Same as writyByte and readByte - Serial.println(char(EEPROM.readChar(address))); + address += sizeof(char); EEPROM.writeUChar(address, 255); // 2^8 - 1 - Serial.println(EEPROM.readUChar(address)); + address += sizeof(unsigned char); EEPROM.writeShort(address, -32768); // -2^15 - Serial.println(EEPROM.readShort(address)); + address += sizeof(short); EEPROM.writeUShort(address, 65535); // 2^16 - 1 - Serial.println(EEPROM.readUShort(address)); + address += sizeof(unsigned short); EEPROM.writeInt(address, -2147483648); // -2^31 - Serial.println(EEPROM.readInt(address)); + address += sizeof(int); EEPROM.writeUInt(address, 4294967295); // 2^32 - 1 - Serial.println(EEPROM.readUInt(address)); + address += sizeof(unsigned int); EEPROM.writeLong(address, -2147483648); // Same as writeInt and readInt - Serial.println(EEPROM.readLong(address)); + address += sizeof(long); EEPROM.writeULong(address, 4294967295); // Same as writeUInt and readUInt - Serial.println(EEPROM.readULong(address)); + address += sizeof(unsigned long); int64_t value = -9223372036854775808; // -2^63 EEPROM.writeLong64(address, value); - value = 0; // Clear value - value = EEPROM.readLong64(value); - Serial.printf("0x%08X", (uint32_t)(value >> 32)); // Print High 4 bytes in HEX - Serial.printf("%08X\n", (uint32_t)value); // Print Low 4 bytes in HEX + address += sizeof(int64_t); uint64_t Value = 18446744073709551615; // 2^64 - 1 EEPROM.writeULong64(address, Value); + address += sizeof(uint64_t); + + EEPROM.writeFloat(address, 1234.1234); + address += sizeof(float); + + EEPROM.writeDouble(address, 123456789.123456789); + address += sizeof(double); + + EEPROM.writeBool(address, true); + address += sizeof(bool); + + String sentence = "I love ESP32."; + EEPROM.writeString(address, sentence); + address += sentence.length() + 1; + + char gratitude[21] = "Thank You Espressif!"; + EEPROM.writeString(address, gratitude); + address += 21; + + // See also the general purpose writeBytes() and readBytes() for BLOB in EEPROM library + EEPROM.commit(); + address = 0; + + Serial.println(EEPROM.readByte(address)); + address += sizeof(byte); + + Serial.println((char)EEPROM.readChar(address)); + address += sizeof(char); + + Serial.println(EEPROM.readUChar(address)); + address += sizeof(unsigned char); + + Serial.println(EEPROM.readShort(address)); + address += sizeof(short); + + Serial.println(EEPROM.readUShort(address)); + address += sizeof(unsigned short); + + Serial.println(EEPROM.readInt(address)); + address += sizeof(int); + + Serial.println(EEPROM.readUInt(address)); + address += sizeof(unsigned int); + + Serial.println(EEPROM.readLong(address)); + address += sizeof(long); + + Serial.println(EEPROM.readULong(address)); + address += sizeof(unsigned long); + + value = 0; + value = EEPROM.readLong64(value); + Serial.printf("0x%08X", (uint32_t)(value >> 32)); // Print High 4 bytes in HEX + Serial.printf("%08X\n", (uint32_t)value); // Print Low 4 bytes in HEX + address += sizeof(int64_t); + Value = 0; // Clear Value Value = EEPROM.readULong64(Value); Serial.printf("0x%08X", (uint32_t)(Value >> 32)); // Print High 4 bytes in HEX Serial.printf("%08X\n", (uint32_t)Value); // Print Low 4 bytes in HEX + address += sizeof(uint64_t); - EEPROM.writeFloat(address, 1234.1234); Serial.println(EEPROM.readFloat(address), 4); + address += sizeof(float); - EEPROM.writeDouble(address, 123456789.123456789); Serial.println(EEPROM.readDouble(address), 8); + address += sizeof(double); - EEPROM.writeBool(address, true); Serial.println(EEPROM.readBool(address)); + address += sizeof(bool); - String sentence = "I love ESP32."; - EEPROM.writeString(address, sentence); Serial.println(EEPROM.readString(address)); + address += sentence.length() + 1; - char gratitude[] = "Thank You Espressif!"; - EEPROM.writeString(address, gratitude); Serial.println(EEPROM.readString(address)); - - // See also the general purpose writeBytes() and readBytes() for BLOB in EEPROM library - // To avoid data overwrite, next address should be chosen/offset by using "address =+ sizeof(previousData)" + address += 21; } void loop() { // put your main code here, to run repeatedly: -} \ No newline at end of file +} diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp index 9ddc0d7e..9fe71460 100644 --- a/libraries/EEPROM/src/EEPROM.cpp +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -1,10 +1,9 @@ /* EEPROM.h -ported by Paolo Becchi to Esp32 from esp8266 EEPROM -Modified by Elochukwu Ifediora + -Converted to nvs lbernstone@gmail.com - Uses a one sector flash partition defined in partition table - OR - Multiple sector flash partitions defined by the name column in the partition table + Uses a nvs byte array to emulate EEPROM Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. @@ -25,72 +24,115 @@ */ #include "EEPROM.h" - +#include #include -EEPROMClass::EEPROMClass(uint32_t sector) - : _sector(sector) - , _data(0) +EEPROMClass::EEPROMClass(void) + : _data(0) , _size(0) , _dirty(false) - , _mypart(NULL) + , _handle(NULL) + , _name("eeprom") + , _user_defined_size(0) +{ +} + +EEPROMClass::EEPROMClass(uint32_t sector) +// Only for compatiility, no sectors in nvs! + : _data(0) + , _size(0) + , _dirty(false) + , _handle(NULL) , _name("eeprom") , _user_defined_size(0) { } EEPROMClass::EEPROMClass(const char* name, uint32_t user_defined_size) - : _sector(0) - , _data(0) + : _data(0) , _size(0) , _dirty(false) - , _mypart(NULL) + , _handle(NULL) , _name(name) , _user_defined_size(user_defined_size) { } -EEPROMClass::EEPROMClass(void) - : _sector(0)// (((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)) - , _data(0) - , _size(0) - , _dirty(false) - , _mypart(NULL) - , _name("eeprom") - , _user_defined_size(0) -{ -} - EEPROMClass::~EEPROMClass() { // end(); } bool EEPROMClass::begin(size_t size) { - if (size <= 0) { - return false; + if (!size) { + return false; } - if (size > SPI_FLASH_SEC_SIZE) { - size = SPI_FLASH_SEC_SIZE; + + 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; } - // _mypart = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,ESP_PARTITION_SUBTYPE_ANY, EEPROM_FLASH_PARTITION_NAME); - _mypart = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, _name); - if (_mypart == NULL) { - 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 (size < key_size) { // truncate + log_w("truncating EEPROM from %d to %d", key_size, size); + uint8_t* key_data = (uint8_t*) malloc(key_size); + if(!key_data) { + log_e("Not enough memory to truncate EEPROM!"); + return false; + } + nvs_get_blob(_handle, _name, key_data, &key_size); + nvs_set_blob(_handle, _name, key_data, size); + nvs_commit(_handle); + free(key_data); + } + else if (size > key_size) { // expand or new + size_t expand_size = size - key_size; + uint8_t* expand_key = (uint8_t*) malloc(expand_size); + if(!expand_key) { + log_e("Not enough memory to expand EEPROM!"); + return false; + } + // check for adequate free space + if(nvs_set_blob(_handle, "expand", expand_key, expand_size)) { + log_e("Not enough space to expand EEPROM from %d to %d", key_size, size); + free(expand_key); + return false; + } + free(expand_key); + nvs_erase_key(_handle, "expand"); + uint8_t* key_data = (uint8_t*) malloc(size); + if(!key_data) { + log_e("Not enough memory to expand EEPROM!"); + return false; + } + memset(key_data, 0, size); + if(key_size) { + log_i("Expanding EEPROM from %d to %d", key_size, size); + // hold data while key is deleted + nvs_get_blob(_handle, _name, key_data, &key_size); + nvs_erase_key(_handle, _name); + } else { + log_i("New EEPROM of %d bytes", size); + } + nvs_commit(_handle); + nvs_set_blob(_handle, _name, key_data, size); + free(key_data); + nvs_commit(_handle); } - size = (size + 3) & (~3); if (_data) { delete[] _data; } _data = new uint8_t[size]; - _size = size; - bool ret = false; - if (esp_partition_read (_mypart, 0, (void *) _data, _size) == ESP_OK) { - ret = true; - } - - return ret; + nvs_get_blob(_handle, _name, _data, &_size); + return true; } void EEPROMClass::end() { @@ -134,29 +176,21 @@ void EEPROMClass::write(int address, uint8_t value) { bool EEPROMClass::commit() { bool ret = false; - if (!_size) - return false; - if (!_dirty) - return true; - if (!_data) - return false; - - - if (esp_partition_erase_range(_mypart, 0, SPI_FLASH_SEC_SIZE) != ESP_OK) - { - log_e( "partition erase err."); + if (!_size) { + return false; } - else - { - if (esp_partition_write(_mypart, 0, (void *)_data, _size) == ESP_ERR_INVALID_SIZE) - { - log_e( "error in Write"); - } - else - { + if (!_data) { + return false; + } + if (!_dirty) { + return true; + } + + if (ESP_OK != nvs_set_blob(_handle, _name, _data, _size)) { + log_e( "error in write"); + } else { _dirty = false; ret = true; - } } return ret; diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h index 3a9165e3..7df7ec7d 100644 --- a/libraries/EEPROM/src/EEPROM.h +++ b/libraries/EEPROM/src/EEPROM.h @@ -1,10 +1,9 @@ /* EEPROM.h -ported by Paolo Becchi to Esp32 from esp8266 EEPROM -Modified by Elochukwu Ifediora + -Converted to nvs lbernstone@gmail.com - Uses a one sector flash partition defined in partition table - OR - Multiple sector flash partitions defined by the name column in the partition table + Uses a nvs byte array to emulate EEPROM Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. @@ -30,19 +29,9 @@ #define EEPROM_FLASH_PARTITION_NAME "eeprom" #endif #include -extern "C" { -#include -#include -#include -#include -} +typedef uint32_t nvs_handle; -// -// need to define AT LEAST a flash partition for EEPROM with above name -// -// eeprom , data , 0x99, start address, 0x1000 -// class EEPROMClass { public: EEPROMClass(uint32_t sector); @@ -117,11 +106,10 @@ class EEPROMClass { template T writeAll (int address, const T &); protected: - uint32_t _sector; + nvs_handle _handle; uint8_t* _data; size_t _size; bool _dirty; - const esp_partition_t * _mypart; const char* _name; uint32_t _user_defined_size; }; diff --git a/tools/partitions/default.csv b/tools/partitions/default.csv index 3e4235d7..e9772b6f 100644 --- a/tools/partitions/default.csv +++ b/tools/partitions/default.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x140000, app1, app, ota_1, 0x150000,0x140000, -eeprom, data, 0x99, 0x290000,0x1000, -spiffs, data, spiffs, 0x291000,0x16F000, +spiffs, data, spiffs, 0x290000,0x170000, diff --git a/tools/partitions/default_16MB.csv b/tools/partitions/default_16MB.csv index 936359b9..7b89daee 100644 --- a/tools/partitions/default_16MB.csv +++ b/tools/partitions/default_16MB.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x640000, app1, app, ota_1, 0x650000,0x640000, -eeprom, data, 0x99, 0xc90000,0x1000, -spiffs, data, spiffs, 0xc91000,0x36F000, \ No newline at end of file +spiffs, data, spiffs, 0xc90000,0x370000, diff --git a/tools/partitions/default_8MB.csv b/tools/partitions/default_8MB.csv index b78b780d..d21c7f6b 100644 --- a/tools/partitions/default_8MB.csv +++ b/tools/partitions/default_8MB.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x330000, app1, app, ota_1, 0x340000,0x330000, -eeprom, data, 0x99, 0x670000,0x1000, -spiffs, data, spiffs, 0x671000,0x18F000, +spiffs, data, spiffs, 0x670000,0x190000, diff --git a/tools/partitions/default_ffat.csv b/tools/partitions/default_ffat.csv index a4a22724..b5bca09d 100644 --- a/tools/partitions/default_ffat.csv +++ b/tools/partitions/default_ffat.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x140000, app1, app, ota_1, 0x150000,0x140000, -eeprom, data, 0x99, 0x290000,0x1000, -ffat, data, fat, 0x291000,0x16F000, +ffat, data, fat, 0x291000,0x170000, diff --git a/tools/partitions/ffat.csv b/tools/partitions/ffat.csv index d91915a6..ed8720bf 100644 --- a/tools/partitions/ffat.csv +++ b/tools/partitions/ffat.csv @@ -3,6 +3,5 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x200000, app1, app, ota_1, 0x210000,0x200000, -eeprom, data, 0x99, 0x410000,0x1000, -ffat, data, fat, 0x411000,0xBEE000, +ffat, data, fat, 0x410000,0xBEF000, # to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage diff --git a/tools/partitions/huge_app.csv b/tools/partitions/huge_app.csv index 7c1c548d..290a7cc7 100644 --- a/tools/partitions/huge_app.csv +++ b/tools/partitions/huge_app.csv @@ -2,5 +2,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x300000, -eeprom, data, 0x99, 0x310000,0x1000, -spiffs, data, spiffs, 0x311000,0xEF000, +spiffs, data, spiffs, 0x310000,0xF0000, diff --git a/tools/partitions/large_spiffs_16MB.csv b/tools/partitions/large_spiffs_16MB.csv index 0ddf14e0..7974c9eb 100644 --- a/tools/partitions/large_spiffs_16MB.csv +++ b/tools/partitions/large_spiffs_16MB.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x480000, app1, app, ota_1, 0x490000,0x480000, -eeprom, data, 0x99, 0x910000,0x1000, -spiffs, data, spiffs, 0x911000,0x6EF000, \ No newline at end of file +spiffs, data, spiffs, 0x910000,0x6F0000, diff --git a/tools/partitions/min_spiffs.csv b/tools/partitions/min_spiffs.csv index 1f7b5e4e..0b6a9ffd 100644 --- a/tools/partitions/min_spiffs.csv +++ b/tools/partitions/min_spiffs.csv @@ -3,5 +3,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x1E0000, app1, app, ota_1, 0x1F0000,0x1E0000, -eeprom, data, 0x99, 0x3D0000,0x1000, -spiffs, data, spiffs, 0x3D1000,0x2F000, +spiffs, data, spiffs, 0x3D0000,0x30000, diff --git a/tools/partitions/minimal.csv b/tools/partitions/minimal.csv index 703fbee3..6ebeeb32 100644 --- a/tools/partitions/minimal.csv +++ b/tools/partitions/minimal.csv @@ -2,5 +2,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x140000, -eeprom, data, 0x99, 0x150000, 0x1000, -spiffs, data, spiffs, 0x151000, 0xAF000, +spiffs, data, spiffs, 0x150000, 0xB0000, diff --git a/tools/partitions/no_ota.csv b/tools/partitions/no_ota.csv index 8b86c231..3314273b 100644 --- a/tools/partitions/no_ota.csv +++ b/tools/partitions/no_ota.csv @@ -2,5 +2,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x200000, -eeprom, data, 0x99, 0x210000,0x1000, -spiffs, data, spiffs, 0x211000,0x1EF000, +spiffs, data, spiffs, 0x210000,0x1F0000, diff --git a/tools/partitions/noota_3g.csv b/tools/partitions/noota_3g.csv index 231c9922..a684385b 100644 --- a/tools/partitions/noota_3g.csv +++ b/tools/partitions/noota_3g.csv @@ -2,5 +2,4 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x100000, -eeprom, data, 0x99, 0x110000,0x1000, -spiffs, data, spiffs, 0x111000,0x2EF000, +spiffs, data, spiffs, 0x110000,0x2F0000, diff --git a/tools/partitions/noota_3gffat.csv b/tools/partitions/noota_3gffat.csv index 1782f68e..f008c277 100644 --- a/tools/partitions/noota_3gffat.csv +++ b/tools/partitions/noota_3gffat.csv @@ -2,6 +2,5 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x100000, -eeprom, data, 0x99, 0x110000,0x1000, -ffat, data, fat, 0x111000,0x2EF000, +ffat, data, fat, 0x110000,0x2F0000, # to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage diff --git a/tools/partitions/noota_ffat.csv b/tools/partitions/noota_ffat.csv index ca5b46ab..69d702f8 100644 --- a/tools/partitions/noota_ffat.csv +++ b/tools/partitions/noota_ffat.csv @@ -2,6 +2,5 @@ nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x200000, -eeprom, data, 0x99, 0x210000,0x1000, -ffat, data, fat, 0x211000,0x1EF000, +ffat, data, fat, 0x210000,0x1F0000, # to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage