Skip to content

Commit

Permalink
Merge pull request #4 from porrey/v1.0.2
Browse files Browse the repository at this point in the history
V1.0.2
  • Loading branch information
porrey authored Jan 15, 2025
2 parents 90ae8fe + ea4122c commit 24d3ea8
Show file tree
Hide file tree
Showing 11 changed files with 661 additions and 632 deletions.
150 changes: 52 additions & 98 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,42 @@
# EEPROM-Storage Library
## Overview
This library is designed to work on both the **Particle** and the **Arduino** platforms.

The EEPROM Storage library provides the ability to access variables stored in EEPROM just as if they were stored in normal RAM. This makes it easy to create static variables that must be restored after a reboot and manage them in your code just like any other variable.


Once defined, a variable can be used in the same manner as its underlying type. For example, a variable defined as an integer (int) would be defined as shown below.


// ***
// *** Define i as an int with the default value of 0;
// ***
int i = 0;

// ***
// *** Set i to 12.
// ***
i = 12;

// ***
// *** Increment i.
// ***
i++;

// ***
// *** Set the value of a to the current value of i.
// ***
int a = i;

This is all very obvious to even the novice programmer but is used here to show the simplicity of the EEPROM Storage class. An integer variable stored in EEPROM would be defined and used in code as shown below.

// ***
// *** Define i as an int with a default value of 10 at EEPROM address 0.
// ***
EEPROMStorage<int> i(0, 10);

// ***
// *** Set i to 12.
// ***
i = 12;

// ***
// *** Increment i.
// ***
i++;

// ***
// *** Set the value of 'a' to the current value of i.
// ***
int a = i;

The first parameter of the constructor defines the address of the variable in EEPROM and the second parameter defines the default value when the EEPROM has been initialized or erased.

After the definition, the variable `i` can be used in the same way as before, but now the value is stored and retrieved to and from EEPROM.
## EEPROM Performance
### Write Limits
True EEPROM chips have write limits. The ATmega328P specifies a 100,000 write limit per address location. Other microcontrollers may have different limits. The Particle Photon implements its EEPROM (for compatibility with existing Arduino code) in static RAM and therefore does not have any write limits. It is good to know your microcontroller specifications when using the EEPROM.
### Flash Memory vs. EEPROM Memory
The read and write speed of EEPROM is much slower than flash memory. When using these EEPROM variables, take note where and when you are reading them and also where and when you are writing them.
### Writing Values
If you attempt to write the current value back to EEPROM, the library will not perform a write. Keep in mind there is extra code to perform this check which can cause some performance issues in your application.
### Reading Values
Reading a value from EEPROM is faster than writing a value to EEPROM, but the read from EEPROM is slower than a variable read from flash memory.
### Tips
The following are recommendations:

1. Avoid excessive writes to a variable
2. Avoid writes in loops where the same variable is updated several times. Instead save the value once at the end of the loop.
3. Avoid reading or writing an EEPROM variable in time sensitive code.
4. Know the write limits for your particular platform/microcontroller. Write your code in such a way that you will not exceed the limit in the lifetime of your application.

## EEPROM Address
### Memory Requirement
## Overview ##
The EEPROM Storage library provides the ability to access variables stored in EEPROM just as if they would stored in normal RAM.

Once defined, a variable can be used in in the same manner as it's underlying type. For example, a variable defined as an integer (int) would be defined as follows:

int i = 0;

Throughout the code this variable can be read and written in various ways. For example, to increment the variable, we would use the statement

i++;

To set the value to a specific value we would, for example, use the statement

i = 12;

This is all very obvious to even the novice programmer, but is used here to show the simplicity of the EEPROM Storage class. An integer variable stored in EEPROM would be defined in the following manner:

EEPROMStorage<int> i(0, 0);

where the first parameter of the constructor defines the address of the variable in EEPROM and the second parameter defines the default value.

After the definition, the variable `i` can be used in the same manner, `i++` and `i = 12` will work the same way they did before, but now the value is stored and retrieved to and from EEPROM.

## EEPROM Address ##
### Memory Requirement ###
When defining an EEPROM Storage variable, it is important to understand the number of bytes required for the underlying type and ensuring that the variables are spaced appropriately so they do not collide.

Each variable requires enough memory to store the underlying type plus one additional byte for a checksum.

Consider the following variable definitions.

EEPROMStorage<uint8_t> v1(0, 0); // 2 bytes (1 + 1 checksum), positions 0 and 1
EEPROMStorage<uint16_t> v2(2, 0); // 3 bytes (2 + 1 checksum), positions 2, 3 and 4
EEPROMStorage<uint32_t> v3(5, 0); // 5 bytes (4 + 1 checksum), positions 5, 6, 6, 8 and 9
EEPROMStorage<float> v4(10, 0.0); // 5 bytes (4 + 1 checksum), positions 10, 11, 12, 13 and 14
EEPROMStorage<bool> v5(15, false); // 2 bytes (1 + 1 checksum), positions 15, 16, 17, 18 and 19
EEPROMStorage<uint8_t> v1(0, 0); // 2 bytes (1 + 1 checksum), positions 0 and 1
EEPROMStorage<uint16_t> v2(2, 0); // 3 bytes (2 + 1 checksum), positions 2, 3 and 4
EEPROMStorage<uint32_t> v3(5, 0); // 5 bytes (4 + 1 checksum), positions 5, 6, 6, 8 and 9
EEPROMStorage<float> v4(10, 0.0); // 5 bytes (4 + 1 checksum), positions 10, 11, 12, 13 and 14
EEPROMStorage<bool> v5(15, false); // 2 bytes (1 + 1 checksum), positions 15, 16, 17, 18 and 19

The best way to think about EEPROM memory is to think about it as a large byte array with a base index of 0. In fact, the Arduino libraries constructs access to EEPROM in this manner.
The best way to think about EEPROM memory is to think about it as a large byte array with a base index of 0. In fact, the Arduino libraries construct access to EEPROM in this manner.

In the above definitions, `v1` is stored at position 0 and occupies two bytes. The first byte is for the data type and the second byte is for the one byte checksum. The variable `v1` requires two bytes and thus occupies EEPROM memory locations 0 and 1,

Expand All @@ -93,6 +45,7 @@ The next variable, `v2`, is located in the position 2, immediately following `v1
> Aligning variables at the beginning of memory or aligning them in a contiguous nature is not required. This just makes it easier, in my opinion, to keep track. You are free to arrange them in any manner that suits your needs.
If you need help determining the proper address for your `EEPROMStorage` instances, open the example sketch called **address.ino** and follow the instructions.

### Determining Data Type Size ###
If you are not sure of the memory requirement for a given data type, you can use the `sizeof` operator. User the Serial port to display the size of any data type.

Expand All @@ -101,49 +54,49 @@ If you are not sure of the memory requirement for a given data type, you can use
When using the `sizeof` operator to determine the number of bytes to preserve remember to add one extra byte for the **checksum**.

To see a full demonstration of this, open the example sketch called **sizeof.ino**.
## EEPROM Storage Initialization
When data has never been stored EEPROM on a micro-controller the memory is in an uninitialized state. Since each byte of memory must have a value between 0 and 255, it is not always possible to detect an uninitialized byte. This behavior could have unexpected side effects if you define a variable and fail to detect whether or not the instance has been initialized.

For this reason, the EEPROM Storage library uses a one-byte checksum to determine if the instance has been initialized or not. When an instance is constructed, a default value is specified. This default value is always returned until a value is explicitly written. The first time a value is written, the variable is initialized and its checksum is calculated. Each write operation to EEPROM will update the checksum.
## Scope
It is important to note that since `EEPROMStorage` variables are stored in the micro-controllers EEPROM. The scope of these variables is always global. In fact, it is possible to instantiate more than one instance using the same address. Both instances will share the same value keeping the two instances in sync.
## EEPROM Storage Initialization ##
When data has never been stored EEPROM on a micro-controller the memory is in an uninitialized state. Since each byte of memory must have a value between 0 and 255, it is not always possible to detect an uninitialized byte. This behavior could have unexpected side effects if your define a variable and fail to detect whether or not the instance has been initialized.

This behavior can be good and bad depending on your scenario. If two instances are created with a different base type, the result may be unexpected.
For this reason, the EEPROM Storage library uses a one byte checksum to determine if the instance has been initialized or not. When an instance is constructed, a default value is specified. This default value is always returned until a value is set thus initializing the location. Each write operation to EEPROM will update the checksum.

> The **EEPROMStorage** variable never caches the value internally and will read the value from EEPROM each time it is requested.
>
> Similarly, each time the instance value is updated it is written directly to EEPROM.
## Scope ##
It is important to note that since `EEPROMStorage` variables are in fact stored in the Micro-controllers EEPROM, the scope of these variables is always global. In fact it is possible to instantiate more than one instance using the same address that as a result will keep the two instances in sync.

# Usage
## Declaration
> The `EEPROMStorage` variable never caches the value internally and will read the value from EEPROM each time it is requested. Similarly, each time the instance value is updated it is written directly to EEPROM.
# Usage #
## Declaration ##
An instance of `EEPROMStorage` can be declared globally, within a class or within a function keeping in mind, as stated previously, that the address controls whether two or more instances share the same value.

The syntax for declaration is as follows:

EEPROMStorage<data type> variableName(address, default value);

### Data Type
### Data Type ###
Specifies the underlying data type for the given instance.

### Address
### Address ###
Specifies the starting index in EEPROM where the value will be stored.

### Default Value
### Default Value ###
Specifies the value that will be returned by the instance when the EEPROM memory location has not been initialized (initialization is determined by the checksum).

### Example
### Example ###
To initialize an instance with an underlying data type of int located in position 50 of the EEPROM and a default value of 10, the syntax would be as follows:

EEPROMStorage<int> myInt(50, 10);
## Assignment

## Assignment ##
Using the previous example, assigning a value to the instance is as simple as the assignment shown here.

myInt = 100;

The `EEPROMStorage` class also defines a `set()` method that can be used.

myInt.set(100);
## Retrieving

## Retrieving ##
To get the instance value, simply assign it to a variable, as shown below,

int x = myInt;
Expand All @@ -155,7 +108,8 @@ or pass it as an argument in any function that takes an int argument as shown be
The `EEPROMStorage` class also defines a `get()` method that can be used.

int x = myInt.get();

## LICENSE
*Copyright 2017-2020 Daniel Porrey*
*Copyright 2017-2025 Daniel Porrey*

*Licensed under the LGPL-3.0 license*
*Licensed under the GPL-3.0 license*
100 changes: 49 additions & 51 deletions examples/address/address.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2017-2020 Daniel Porrey. All Rights Reserved.
// Copyright © 2017-2025 Daniel Porrey. All Rights Reserved.
//
// This file is part of the EEPROM-Storage library.
//
Expand All @@ -17,20 +17,18 @@
// see http://www.gnu.org/licenses/.
//

// ******************************************************************************************
// *** This example will calculate the address and
// *** create initialization code for one or more
// *** EEPROM-Storage variables. Just follow the steps
// *** outlined below.
// ******************************************************************************************

// ******************************************************************************************
// ******************************************************************************************
// ***
// *** Complete Steps 1 through 3 (below) for your project.
// ***
// ******************************************************************************************
// ******************************************************************************************
// ***************************************************************************************
// This example will calculate the address and
// create initialization code for one or more
// EEPROM-Storage variables. Just follow the steps
// outlined below.
// ***************************************************************************************

// ***************************************************************************************
//
// Complete Steps 1 through 3 (below) for your project.
//
// ***************************************************************************************

#if defined(SPARK)
#define word short
Expand Down Expand Up @@ -87,22 +85,22 @@ struct definition

void setup()
{
// ***
// *** Initialize the serial port.
// ***
//
// Initialize the serial port.
//
Serial.begin(115200);

// ***
// *** Wait for serial port to connect. Needed
// *** for native USB port only
// ***
//
// Wait for serial port to connect. Needed
// for native USB port only
//
while (!Serial);

// ***
// *** STEP 1 of 3:
// ***
// *** Create a list of definitions that each contain the data type and the default value.
// ***
//
// STEP 1 of 3:
//
// Create a list of definitions that each contain the data type and the default value.
//
definition myDefinitions[] =
{
{ DT_INT, 0 },
Expand All @@ -113,28 +111,28 @@ void setup()
{ DT_INT, 10 }
};

// ***
// *** STEP 2 of 3:
// ***
// *** Define the starting address in EEPROM. If you are manually string other data in EEPROM this
// *** address could be defined at the end of that data.
// ***
//
// STEP 2 of 3:
//
// Define the starting address in EEPROM. If you are manually string other data in EEPROM this
// address could be defined at the end of that data.
//
int myFirstAddress = 0;

// ***
// *** STEP 3 of 3:
// ***
// *** Set the value below to true to display comments and false to hide the comments.
// ***
//
// STEP 3 of 3:
//
// Set the value below to true to display comments and false to hide the comments.
//
bool showComments = true;

// ******************************************************************************************
// ******************************************************************************************
// ***
// *** This will output declarations through the serial port.
// ***
// ******************************************************************************************
// ******************************************************************************************
//***************************************************************************************
//***************************************************************************************
//
// This will output declarations through the serial port.
//
//***************************************************************************************
//***************************************************************************************
int items = sizeof(myDefinitions) / sizeof(definition);
Serial.print("The number of declarations defined is "); Serial.println(items);
Serial.println();
Expand All @@ -144,9 +142,9 @@ void setup()
{
if (showComments)
{
Serial.println("// ***");
Serial.print("// *** Total length = "); Serial.print(_size[myDefinitions[i].type] + 1); Serial.print(" [Addresses "); Serial.print(nextAddress); Serial.print(" through "); Serial.print(nextAddress + _size[myDefinitions[i].type]); Serial.println("]");
Serial.println("// ***");
Serial.println("//");
Serial.print("// Total length = "); Serial.print(_size[myDefinitions[i].type] + 1); Serial.print(" [Addresses "); Serial.print(nextAddress); Serial.print(" through "); Serial.print(nextAddress + _size[myDefinitions[i].type]); Serial.println("]");
Serial.println("//");
}

Serial.print("EEPROMStorage<"); Serial.print(_name[myDefinitions[i].type]); Serial.print(">("); Serial.print(nextAddress); Serial.print(", "); Serial.print(myDefinitions[i].defaultValue); Serial.println(");");
Expand All @@ -158,8 +156,8 @@ void setup()

void loop()
{
// ***
// *** Delay 2 seconds.
// ***
//
// Delay 2 seconds.
//
delay(2000);
}
Loading

0 comments on commit 24d3ea8

Please sign in to comment.