diff --git a/libraries/ESP_EEPROM/.gitattributes b/libraries/ESP_EEPROM/.gitattributes new file mode 100644 index 0000000000..dfe0770424 --- /dev/null +++ b/libraries/ESP_EEPROM/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/libraries/ESP_EEPROM/.gitignore b/libraries/ESP_EEPROM/.gitignore new file mode 100644 index 0000000000..e43b0f9889 --- /dev/null +++ b/libraries/ESP_EEPROM/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/libraries/ESP_EEPROM/README.md b/libraries/ESP_EEPROM/README.md new file mode 100644 index 0000000000..b916e6755a --- /dev/null +++ b/libraries/ESP_EEPROM/README.md @@ -0,0 +1,6 @@ +# ESP_EEPROM +An improved EEPROM emulation Arduino library for ESP8266 + +The ESP8266 family doesn't have genuine EEPROM memory so for Arduino it is normally emulated by using a section of flash memory. + +With the standard ESP8266 EEPROM library, the sector needs to be re-flashed every time the changed EEPROM data needs to be saved. For small amounts of EEPROM data this is very slow and will wear out the flash memory more quickly. This library writes a new copy of your data when you save (commit) it and keeps track of where in the sector the most recent copy is kept using a bitmap. The flash sector only needs to be erased when there is no more space for copies in the flash sector. You can keep track of this yourself to do a time-consuming erase when most convenient or the library will do it for you when there is no more space for the data when you commit it. diff --git a/libraries/ESP_EEPROM/examples/ESP_EEPROM_CommitReset/ESP_EEPROM_CommitReset.ino b/libraries/ESP_EEPROM/examples/ESP_EEPROM_CommitReset/ESP_EEPROM_CommitReset.ino new file mode 100644 index 0000000000..be28fe0958 --- /dev/null +++ b/libraries/ESP_EEPROM/examples/ESP_EEPROM_CommitReset/ESP_EEPROM_CommitReset.ino @@ -0,0 +1,79 @@ +// Example use of ESP_EEPROM library for ESP8266 +// +// Normally writing to the 'emulated' EEPROM on ESP8266 requires an erase of the flash page used to hold +// the EEPROM data followed by a re-write of the changed data. +// The erasure takes a significant amount of time (10s of ms) and during this time +// interrupts must be blocked. +// In some cases this interferes with the sketch operation (e.g. it produces a noticeable +// blackout/flash for any PWM controlled lights as ESP8266 PWM relies on interrupts) +// +// The ESP_EEPROM library writes each new version of the EEPROM data to a new area until flash +// is full and so avoids wiping flash until necessary +// +// It's best for use when there are only a few things to save in EEPROM +// (i.e. total the size of the saved info is much smaller than the available flash size) +// + +#include + +const int BLUE_LED_PIN = 2; + +// The neatest way to access variables stored in EEPROM is using a structure +struct MyEEPROMStruct { + int anInteger; + float aFloating; + int anotherInteger; + byte someBytes[12]; + boolean state; +} eepromVar1, eepromVar2; + +void setup() { + // Remember to set your serial monitor to 74880 baud + // This odd speed will show ESP8266 boot diagnostics too + Serial.begin(74880); + Serial.println(); + + // Set up the initial (default) values for what is to be stored in EEPROM + eepromVar1.anInteger = 99; + eepromVar1.aFloating = 99.99; + eepromVar1.anotherInteger = 42; + eepromVar1.state = true; + + // All the library functions are accessed via the EEPROM object created when + // you include the library header ESP_EEPROM.h + + // The library needs to know what size you need for your EEPROM variables + // Using a structure makes this easy. + + // The begin() call is required to initialise the EEPROM library + EEPROM.begin(sizeof(MyEEPROMStruct)); + + // + // (some code that might change the EEPROM data) + // + + // set the EEPROM data ready for writing + EEPROM.put(0, eepromVar1); + + // write the data to EEPROM - ignoring anything that might be there already (re-flash is guaranteed) + boolean ok1 = EEPROM.commitReset(); + Serial.println((ok1) ? "Commit (Reset) OK" : "Commit failed"); + + // + // (some code that might change the EEPROM data some more) + // + eepromVar1.anInteger++; // Change some data + + // set the EEPROM data ready for writing + EEPROM.put(0, eepromVar1); + + // write the data to EEPROM + boolean ok2 = EEPROM.commit(); + Serial.println((ok2) ? "Commit OK" : "Commit failed"); + +} + + +void loop() { + // do nothing +} diff --git a/libraries/ESP_EEPROM/examples/ESP_EEPROM_Simple/ESP_EEPROM_Simple.ino b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Simple/ESP_EEPROM_Simple.ino new file mode 100644 index 0000000000..5210d9b807 --- /dev/null +++ b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Simple/ESP_EEPROM_Simple.ino @@ -0,0 +1,71 @@ +// Example use of ESP_EEPROM library for ESP8266 +// +// Normally writing to the 'emulated' EEPROM on ESP8266 requires an erase of the flash page used to hold +// the EEPROM data followed by a re-write of the changed data. +// The erasure takes a significant amount of time (10s of ms) and during this time +// interrupts must be blocked. +// In some cases this interferes with the sketch operation (e.g. it produces a noticeable +// blackout/flash for any PWM controlled lights as ESP8266 PWM relies on interrupts) +// +// The ESP_EEPROM library writes each new version of the EEPROM data to a new area until flash +// is full and so avoids wiping flash until necessary +// +// It's best for use when there are only a few things to save in EEPROM +// (i.e. total the size of the saved info is much smaller than the available flash size) +// + +#include + +int eepromVar1 = 0; +long eepromVar2 = 1234; + +void setup() { + // Remember to set your serial monitor to 74880 baud + // This odd speed will show ESP8266 boot diagnostics too + Serial.begin(74880); + Serial.println(); + + // All the library functions are accessed via the EEPROM object created when + // you include the library header ESP_EEPROM.h + + // The library needs to know what size you need for your EEPROM variables + // The minimum size is 16 + + // The begin() call is required to initialise the EEPROM library + EEPROM.begin(16); + + // put some data into eeprom + EEPROM.put(0, eepromVar1); // int - so 4 bytes (next address is '4') + EEPROM.put(4, eepromVar2); // long - so 8 bytes (next address would be '12') + + // write the data to EEPROM + boolean ok1 = EEPROM.commit(); + Serial.println((ok1) ? "First commit OK" : "Commit failed"); + + // The eeprom data gets changed + eepromVar1 = 1; // Change some data + + // set the EEPROM data ready for writing + EEPROM.put(0, eepromVar1); + + // commit (write) the data to EEPROM - only actually writes if there has been a change + boolean ok2 = EEPROM.commit(); + Serial.println((ok2) ? "Second commit OK" : "Commit failed"); + + // How to read stuff back into variables + // variables should be same size as originally written + int aNewVar1; + long aNewVar2; + EEPROM.get(0, aNewVar1); + EEPROM.get(4, aNewVar2); + Serial.print("Read back a variable 1 from EEPROM: "); + Serial.println(aNewVar1); + Serial.print("Read back a variable 2 from EEPROM: "); + Serial.println(aNewVar2); + +} + + +void loop() { + delay(1000); +} diff --git a/libraries/ESP_EEPROM/examples/ESP_EEPROM_Use/ESP_EEPROM_Use.ino b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Use/ESP_EEPROM_Use.ino new file mode 100644 index 0000000000..cdd2b51b49 --- /dev/null +++ b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Use/ESP_EEPROM_Use.ino @@ -0,0 +1,87 @@ +// Example use of ESP_EEPROM library for ESP8266 +// +// Normally writing to the 'emulated' EEPROM on ESP8266 requires an erase of the flash page used to hold +// the EEPROM data followed by a re-write of the changed data. +// The erasure takes a significant amount of time (10s of ms) and during this time +// interrupts must be blocked. +// In some cases this interferes with the sketch operation (e.g. it produces a noticeable +// blackout/flash for any PWM controlled lights as ESP8266 PWM relies on interrupts) +// +// The ESP_EEPROM library writes each new version of the EEPROM data to a new area until flash +// is full and so avoids wiping flash until necessary +// +// It's best for use when there are only a few things to save in EEPROM +// (i.e. total the size of the saved info is much smaller than the available flash size) +// + +#include + +const int BLUE_LED_PIN = 2; + +// The neatest way to access variables stored in EEPROM is using a structure +struct MyEEPROMStruct { + int anInteger; + float aFloating; + int anotherInteger; + byte someBytes[12]; + boolean state; +} eepromVar1, eepromVar2; + +void setup() { + // Remember to set your serial monitor to 74880 baud + // This odd speed will show ESP8266 boot diagnostics too + Serial.begin(74880); + Serial.println(); + + // Set up the initial (default) values for what is to be stored in EEPROM + eepromVar1.anInteger = 99; + eepromVar1.aFloating = 99.99; + eepromVar1.anotherInteger = 42; + eepromVar1.state = true; + + // All the library functions are accessed via the EEPROM object created when + // you include the library header ESP_EEPROM.h + + // The library needs to know what size you need for your EEPROM variables + // Using a structure makes this easy. + + // The begin() call will find the data previously saved in EEPROM if the same size + // as was previously committed. If the size is different then the EEEPROM data is cleared. + // Note that this is not made permanent until you call commit(); + EEPROM.begin(sizeof(MyEEPROMStruct)); + + // Check if the EEPROM contains valid data from another run + // If so, overwrite the 'default' values set up in our struct + if(EEPROM.percentUsed()!=0) { + EEPROM.get(0, eepromVar1); + eepromVar1.anInteger++; // make a change to our copy of the EEPROM data + Serial.println("EEPROM has data from a previous run."); + Serial.print(EEPROM.percentUsed()); + Serial.println("% of ESP flash space currently used"); + } else { + Serial.println("EEPROM size changed - EEPROM data zeroed - commit() to make permanent"); + } + + // + // (some code that might change the EEPROM data) + // + + // set the EEPROM data ready for writing + EEPROM.put(0, eepromVar1); + + // write the data to EEPROM + boolean ok = EEPROM.commit(); + Serial.println((ok) ? "Commit OK" : "Commit failed"); + + // Get EEPROM data into our local copy + // For this example, a different struct variable is used + EEPROM.get(0, eepromVar2); + + Serial.print("EEPROM data read, anInteger="); + Serial.println(eepromVar2.anInteger); +} + + +void loop() { + // do nothing +} diff --git a/libraries/ESP_EEPROM/examples/ESP_EEPROM_Wipe/ESP_EEPROM_Wipe.ino b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Wipe/ESP_EEPROM_Wipe.ino new file mode 100644 index 0000000000..fa2a6b385f --- /dev/null +++ b/libraries/ESP_EEPROM/examples/ESP_EEPROM_Wipe/ESP_EEPROM_Wipe.ino @@ -0,0 +1,36 @@ +// Example use of ESP_EEPROM library for ESP8266 +// +// This simple sketch wipes (erases) the flash storage area used +// to hold the EEPROM data. +// +// Useful when testing to ensure your code deals with a 'new' device +// as well as one hilding previous EEPROM data. +// + +#include + +const int BLUE_LED_PIN = 2; + +void setup() { + // Remember to set your serial monitor to 74880 baud + // This odd speed will show ESP8266 boot diagnostics too + Serial.begin(74880); + Serial.println(); + + // It is still necessary to call begin + // or no wipe will be performed + EEPROM.begin(EEPROM_MIN_SIZE); + + + boolean result = EEPROM.wipe(); + if (result) { + Serial.println("All EEPROM data wiped"); + } else { + Serial.println("EEPROM data could not be wiped from flash store"); + } +} + + +void loop() { + // do nothing +} diff --git a/libraries/ESP_EEPROM/keywords.txt b/libraries/ESP_EEPROM/keywords.txt new file mode 100644 index 0000000000..362db019e9 --- /dev/null +++ b/libraries/ESP_EEPROM/keywords.txt @@ -0,0 +1,31 @@ +####################################### +# Syntax Colouring Map For ESP_EEPROM +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP_EEPROM KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +doSomething KEYWORD2 +begin KEYWORD2 +read KEYWORD2 +write KEYWORD2 +commit KEYWORD2 +commitReset KEYWORD2 +wipe KEYWORD2 +percentUsed KEYWORD2 +end KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### \ No newline at end of file diff --git a/libraries/ESP_EEPROM/library.properties b/libraries/ESP_EEPROM/library.properties new file mode 100755 index 0000000000..2cbe4a8ed2 --- /dev/null +++ b/libraries/ESP_EEPROM/library.properties @@ -0,0 +1,9 @@ +name=ESP_EEPROM +version=1.0.1 +author=j-watson +maintainer=j-watson +sentence=An improved EEPROM library for ESP8266 +paragraph=The ESP8266 family doesn't have genuine EEPROM memory so it is normally emulated by using a section of flash memory. With the standard library, the sector needs to be re-flashed every time the changed EEPROM data needs to be saved. For small amounts of EEPROM data this is very slow and will wear out the flash memory more quickly. This library writes a new copy of your data when you save (commit) it and keeps track of where in the sector the most recent copy is kept. The flash sector only needs to be erased when there is no more space for copies in the flash sector. You can keep track of this yourself to do a time-consuming erase when most convenient or the library will do it for you when there is no more space for the data when you commit it. +category=Data Storage +architectures=* +url=https://github.com/jwrw/ESP_EEPROM.git diff --git a/libraries/ESP_EEPROM/src/ESP_EEPROM.cpp b/libraries/ESP_EEPROM/src/ESP_EEPROM.cpp new file mode 100644 index 0000000000..5646d31015 --- /dev/null +++ b/libraries/ESP_EEPROM/src/ESP_EEPROM.cpp @@ -0,0 +1,359 @@ +/* + ESP_EEPROM.cpp - esp8266 EEPROM emulation + + Copyright (c) 2018 James Watson. All rights reserved. + + Based on ESP8266 EEPROM library, part of standard + esp8266 core for Arduino environment by Ivan Grokhotkov. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Avoid the significant period with no interrupts required for flash erasure + - and avoid unnecessary re-flashing + + >>> Layout <<< + 4 bytes - size of a block + bitmap - bit 0 never written - shows state of flash after erase + subsequent bits are set to the opposite for each block containing data + the highest block is the latest version + Data versions follow consecutively - size rounded up to 4 byte boundaries + and a minimum size + */ + + +#include "Arduino.h" +#include "ESP_EEPROM.h" + +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "spi_flash.h" +} + +extern "C" uint32_t _SPIFFS_end; + +//------------------------------------------------------------------------------ +EEPROMClass::EEPROMClass(uint32_t sector): +_sector(sector), +_data(0), +_size(0), +_bitmapSize(0), +_bitmap(0), +_offset(0), +_dirty(false) +{ +} + +//------------------------------------------------------------------------------ +EEPROMClass::EEPROMClass(void) : +_sector((((uint32_t) & _SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)), +_data(0), +_size(0), +_bitmapSize(0), +_bitmap(0), +_offset(0), +_dirty(false) +{ +} + +//------------------------------------------------------------------------------ +void EEPROMClass::begin(size_t size) { + _dirty = true; + if (size <= 0 || size > (SPI_FLASH_SEC_SIZE - 8)) { + // max size is smaller by 4 bytes for size and 4 byte bitmap - to keep 4 byte aligned + return; + } else if (size < EEPROM_MIN_SIZE) { + size = EEPROM_MIN_SIZE; + } + + size = (size + 3) & ~3; // align to 4 bytes + _bitmapSize = computeBitmapSize(size); + + // drop any old allocation and re-allocate buffers + if (_bitmap) { + delete[] _bitmap; + } + _bitmap = new uint8_t[_bitmapSize]; + if (_data) { + delete[] _data; + } + _data = new uint8_t[size]; + + noInterrupts(); + spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(&_size), 4); + interrupts(); + + if (_size != size) { + // flash structure is all wrong - will need to re-do + _size = size; + _offset = 0; // offset of zero => flash data is garbage + + } else { + // Size is correct so get bitmap/data from flash + // First read the bitmap from flash + noInterrupts(); + spi_flash_read(_sector * SPI_FLASH_SEC_SIZE + 4, reinterpret_cast(_bitmap), _bitmapSize); + interrupts(); + + // flash should contain a good version of the data - find it using the bitmap + _offset = offsetFromBitmap(); + + if (_offset == 0 || _offset + _size > SPI_FLASH_SEC_SIZE) { + // something is screwed up + // flag that _data[] is bad / uninitialised + _offset = 0; + } else { + noInterrupts(); + spi_flash_read(_sector * SPI_FLASH_SEC_SIZE + _offset, reinterpret_cast(_data), _size); + interrupts(); + + // all good + _dirty = false; + } + } +} + +//------------------------------------------------------------------------------ +int EEPROMClass::percentUsed() { + if(_offset == 0 || _size==0) return 0; + else { + int nCopies = (SPI_FLASH_SEC_SIZE - 4 - _bitmapSize) / _size; + int copyNo = 1 + (_offset - 4 - _bitmapSize) / _size; + return (100 * copyNo) / nCopies; + } +} + +//------------------------------------------------------------------------------ +void EEPROMClass::end() { + if (!_size) + return; + + commit(); + if (_data) { + delete[] _data; + } + if (_bitmap) { + delete[] _bitmap; + } + _bitmap = 0; + _bitmapSize = 0; + _data = 0; + _size = 0; + _dirty = false; +} + +//------------------------------------------------------------------------------ +uint8_t EEPROMClass::read(int const address) { + if (address < 0 || (size_t)address >= _size) + return 0; + if (!_data) + return 0; + + return _data[address]; +} + +//------------------------------------------------------------------------------ +void EEPROMClass::write(int const address, uint8_t const value) { + if (address < 0 || (size_t)address >= _size) + return; + if (!_data) + return; + + // Optimise _dirty. Only flagged if data written is different. + if (_data[address] != value) + { + _data[address] = value; + _dirty = true; + } +} + +//------------------------------------------------------------------------------ +bool EEPROMClass::commitReset() { + // set an offset that ensures flash will be erased before commit + uint32_t oldOffset = _offset; // if commit fails, _offset won't be updated + _offset = SPI_FLASH_SEC_SIZE; + _dirty = true; // ensure writing takes place + if ( commit() ) { + return (true); + } else { + _offset = oldOffset; + return (false); + } +} + +//------------------------------------------------------------------------------ +bool EEPROMClass::commit() { + // everything has to be in place to even try a commit + if (!_size || !_dirty || !_data || !_bitmap || _bitmapSize == 0) { + return false; + } + + SpiFlashOpResult flashOk = SPI_FLASH_RESULT_OK; + uint32_t oldOffset = _offset; // if write fails, _offset won't be updated + + // If initial version or not enough room for new version, erase and start anew + if (_offset == 0 || _offset + _size + _size > SPI_FLASH_SEC_SIZE) { + + noInterrupts(); + flashOk = spi_flash_erase_sector(_sector); + interrupts(); + if (flashOk != SPI_FLASH_RESULT_OK) { + return false; + } + + // write size + noInterrupts(); + flashOk = spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(&_size), 4); + interrupts(); + if (flashOk != SPI_FLASH_RESULT_OK) { + return false; + } + + // read first 4 bytes of bitmap + noInterrupts(); + spi_flash_read(_sector * SPI_FLASH_SEC_SIZE + 4, reinterpret_cast(_bitmap), 4); + interrupts(); + + // init the rest of the _bitmap based on value of first byte + for (int i = 4; i < _bitmapSize; i++ ) _bitmap[i] = _bitmap[0]; + + // all reset ok - point to where the data needs to go + _offset = 4 + _bitmapSize; + } else { + _offset += _size; + } + + noInterrupts(); + flashOk = spi_flash_write(_sector * SPI_FLASH_SEC_SIZE + _offset, reinterpret_cast(_data), _size); + interrupts(); + + if (flashOk != SPI_FLASH_RESULT_OK) { + _offset = oldOffset; + return false; + } + + // Data written OK so need to update bitmap + int bitmapByteUpdated = flagUsedOffset(); + + bitmapByteUpdated &= ~3; // align to 4 byte for write + noInterrupts(); + flashOk = spi_flash_write(_sector * SPI_FLASH_SEC_SIZE + bitmapByteUpdated + 4, reinterpret_cast(&_bitmap[bitmapByteUpdated]), 4); + interrupts(); + if (flashOk != SPI_FLASH_RESULT_OK) { + return false; + } + + // all good! + interrupts(); + _dirty = false; + return true; +} + +//------------------------------------------------------------------------------ +// Force an immedate erase of the flash sector - but nothing is written +// Will need a commit() to write structure (size and bitmap etc.) +// but does re-intitialise internal storage +bool EEPROMClass::wipe() { + if(_size==0 || _bitmapSize==0) return false; // must have called begin() + + // drop any old allocation and re-allocate buffers + if (_bitmap) { + delete[] _bitmap; + } + _bitmap = new uint8_t[_bitmapSize]; + if (_data) { + delete[] _data; + } + _data = new uint8_t[_size]; + + noInterrupts(); + SpiFlashOpResult flashOk = spi_flash_erase_sector(_sector); + interrupts(); + + // flash is clear - need a commit() to write structure (size and bitmap etc.) + _dirty = true; + _offset = 0; + return (flashOk==SPI_FLASH_RESULT_OK); +} + +//------------------------------------------------------------------------------ +// Compute the offset of the current version of data using the bitmap +uint16_t EEPROMClass::offsetFromBitmap() { + + if (!_bitmap || _bitmapSize <= 0) return 0; + + uint16_t offset = 4 + _bitmapSize; + boolean flash = (_bitmap[0] & 1); // true => 'after flash' state is 1 (else it must be 0) + + // Check - the very first entry in the bitmap should indicate a valid _data + if ((flash && ((_bitmap[0] & 2) != 0)) || (!flash && ((_bitmap[0] & 2) == 0)) ) { + // something's wrong - Bitmap doesn't have bit recording first data version + return 0; + } + + for (int bmByte = 0; bmByte < _bitmapSize; bmByte++) { + for (int bmBit = (bmByte == 0) ? 4 : 1; bmBit < 0x0100; bmBit <<= 1) { + // looking for bit state that matches the 'after flash' state (i.e. first untouched bit) + if ((flash && ((_bitmap[bmByte] & bmBit) != 0)) || (!flash && ((_bitmap[bmByte] & bmBit) == 0)) ) { + return offset; // offset pointed at last written + } else { + offset += _size; + } + } + } + + // dropped off the bottom - return the offset - but it will be useless + return offset; +} + +//------------------------------------------------------------------------------ +// flag within the bitmap the appropriate bit for the current _data version at _offset +// return the byte index within _bitmap that has been changed +int EEPROMClass::flagUsedOffset() { + int bitNo = 1 + (_offset - 4 - _bitmapSize) / _size; + int byteNo = bitNo >> 3; + + uint8_t bitMask = 1 << (bitNo & 0x7); + if (_bitmap[0] & 1) { + // need to clear the bitmap bit + _bitmap[byteNo] &= ~bitMask; + } else { + // need to set the bitmap bit + _bitmap[byteNo] |= bitMask; + } + + return byteNo; +} + +//------------------------------------------------------------------------------ +// Computing size of bitmap needed for the number of copies that can be held +uint16_t EEPROMClass::computeBitmapSize(size_t size) { + + // With 1 bit in bitmap and 8 bits per byte + // This is the max number of copies possible + uint32_t nCopies = ((SPI_FLASH_SEC_SIZE - 4L) * 8L - 1L) / (size * 8L + 1L); + + // applying alignment constraints - this is the bitmap size needed + uint32_t bitmapSize = (((nCopies + 1L) + 31L) / 8L) & ~3; + + return bitmapSize & 0x7fff; +} + +//------------------------------------------------------------------------------ +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) +EEPROMClass EEPROM; +#endif diff --git a/libraries/ESP_EEPROM/src/ESP_EEPROM.h b/libraries/ESP_EEPROM/src/ESP_EEPROM.h new file mode 100644 index 0000000000..5547c025a8 --- /dev/null +++ b/libraries/ESP_EEPROM/src/ESP_EEPROM.h @@ -0,0 +1,94 @@ +/* + ESP_EEPROM.cpp - improved esp8266 EEPROM emulation + + Copyright (c) 2018 James Watson. All rights reserved. + + Based on API defined for ESP8266 EEPROM library, part of standard + esp8266 core for Arduino environment by Ivan Grokhotkov. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ESP_EEPROM_h +#define ESP_EEPROM_h + +#include +#include + +// If you are using a tiny amount then allocation map takes a lot of room and a long time to check +// so minimum size is limited +const size_t EEPROM_MIN_SIZE = 16; + +class EEPROMClass { +public: + + EEPROMClass(void); + + void begin(size_t size); + uint8_t read(int const address); + void write(int const address, uint8_t const val); + bool commit(); + bool commitReset(); + bool wipe(); + int percentUsed(); + void end(); + + template + T &get(int const address, T &t) { + if (_data && (address >= 0) && (address + sizeof(T) <= _size)) { + memcpy((uint8_t*) &t, _data + address, sizeof(T)); + } + return t; + } + + template + const T &put(int const address, const T &t) { + if (_data && (address >= 0) && (address + sizeof(T) <= _size)) { + + // only flag as dirty and copied if different - if already dirty, just get on with copy + if (_dirty || memcmp(_data + address, (const uint8_t*)&t, sizeof(T)) != 0) { + _dirty = true; + memcpy(_data + address, (const uint8_t*)&t, sizeof(T)); + } + } + return t; + } + + size_t length() { + return _size; + } + +protected: + EEPROMClass(uint32_t sector); + + uint32_t _sector; + uint8_t* _data; + uint32_t _size; + uint8_t* _bitmap; + uint16_t _bitmapSize; + uint16_t _offset; + bool _dirty; + + uint16_t offsetFromBitmap(); + int flagUsedOffset(); + uint16_t computeBitmapSize(size_t size); +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) +extern EEPROMClass EEPROM; +#endif + +#endif +