By now, we should know that when Arduino reboots, all the memory where we store variables is erased, so it’s not possible to keep the values.
However, sometimes we want certain values to persist between reboots. For example, calibration values, measurements and dates or times for dataloggers, saving a counter, or knowing the processor’s state when it lost power, among many others.
For these purposes, Arduino incorporates non-volatile storage memory called EEPROM (Electrically Erasable Programable Read-Only Memory).
Therefore, in Arduino we have three types of memory:
- FLASH, non-volatile, where we write the sketch (including the bootloader).
- SRAM (static random access memory), volatile, where variables are stored during operation.
- EEPROM, non-volatile, which we can use to store information between reboots.
EEPROM Characteristics
EEPROM memory has its own characteristics and peculiarities that distinguish it from other memories. First, and most obvious, it is non-volatile (i.e., it maintains stored values when power is lost).
On the other hand, EEPROM memory is a more scarce resource than other memories. Most Arduino models have 1KB, while the Mega has 4KB.
A disadvantage of EEPROM memory is that it is much slower than SRAM memory. The process of writing a cell (byte) takes about 3.3 ms. The reading process is much faster (although still slower than SRAM); reading 1024 bytes takes about 0.3ms, i.e., 10,000 times faster than writing.
Another peculiarity of EEPROM memory is that it has a limited lifespan, which decreases with each write operation. There are no limits for read operations.
Specifications guarantee that each cell has a lifespan of at least 100,000 operations. Although in practice it can be much higher, up to a million operations, above 100,000 operation is not guaranteed.
100,000 write operations may seem like a lot, but consider that it’s:
- Barely 5 minutes if, by mistake, we write constantly.
- Approximately one day if we write every second.
- About 27 years if we write 10 times a day
That is, EEPROM memory is designed for writes with long intervals between them, not constant use.
Functions for Using EEPROM
The Standard Arduino IDE includes an EEPROM.h library that provides the necessary functions to manipulate Arduino’s non-volatile memory.
The simplest functions are the Read and Write functions, which respectively read and write a byte at a memory address.
The memory address can have values from 0 to N-1, where N is the number of available bytes (e.g., 0 to 1023 in Arduino Uno and Nano, 0 to 4095 in Arduino Mega).
//Reads a single byte from the address
EEPROM.Read(address)
//Writes a single byte to the address
EEPROM.Write(address)
The above functions write a single byte, but often we will need to save variables that are larger than one byte. For this, we have the Put, Get, and Update functions, which we will use most frequently.
// Functions for complete variables (takes the size of the variable into account)
//Reads a variable at the address
EEPROM.Put(address, variable)
//Reads a variable at the address
EEPROM.Get(address, variable)
EEPROM.Update(address, variable) //Updates the value of a variable, that is, it first reads it, and only writes it if its value is different from the one we are going to store. This helps reduce the number of writes and extend the lifespan of the memory.
The Put, Get, and Update functions take into account the variable’s size and work even with variables and structures defined by us. However, we will have to consider the variable’s size to know the next address to write to and avoid variables “overlapping.”
Code Examples
Iterate Through the Entire EEPROM
In the first example, we use the EEPROM library to write 0 to the entire memory. In general, we won’t use this type of code because it involves a write operation on the entire memory, which, remember, has a limited write cycle.
But the goal is to present the use of the EEPROM.length() function to get the amount of available memory (regardless of the model) and the use of EEPROM[index] to access memory positions.
#include <EEPROM.h>
void setup()
{
for( int index = 0 ; index < EEPROM.length() ; index++ )
{
EEPROM[ index ] = 0;
}
}
void loop()
{
}
Write a Variable to EEPROM
In this example, we perform the writing of a variable. In the example, we write a float variable, but it could be any other type of variable. We use a ReadSensor() function that emulates a function that could, for example, read a temperature sensor, a power sensor, or any other type of sensor.
Note the use of the sizeof() and length() functions to get the position of the next cell to write.
#include <EEPROM.h>
float sensorValue;
int eeAddress = 0;
//Function that simulates reading a sensor
float ReadSensor()
{
return 10.0f;
}
void setup()
{
}
void loop()
{
sensorValue = ReadSensor(); //Simulated sensor reading
EEPROM.put( eeAddress, sensorValue ); //Write the value
eeAddress += sizeof(float); //Get the next position to write
if(eeAddress >= EEPROM.length()) eeAddress = 0; //Check for overflow
delay(30000); //wait 30 seconds
}
Write a Structure to EEPROM
In C++ and, therefore, in Arduino, we can define our own variable types and structures. The EEPROM functions work equally with these custom variables.
In this example, we write a structure, similarly to any other variable.
#include <EEPROM.h>
struct MyStruct{
float field1;
byte field2;
char name[10];
};
void setup()
{
int eeAddress = 0;
MyStruct customVar = {
3.14f,
65,
"Working!"
};
eeAddress += sizeof(MyStruct);
EEPROM.put( eeAddress, customVar );
}
void loop()
{
}
Using Update to Update Values
As mentioned, the Update() function checks the existing value in memory before writing and only writes if the value is different from the stored one.
This implies a small performance loss (small, because the read operation is much faster than the write) but helps extend the memory’s lifespan.
In the following example, the analog reading A0 is written to EEPROM.
#include <EEPROM.h>
int eeAddress = 0;
void setup()
{
}
void loop()
{
int val = analogRead(0) / 4;
EEPROM.update(eeAddress, val);
eeAddress += sizeof(int);
if(eeAddress == EEPROM.length()) eeAddress = 0;
delay(10000); //Wait for 10 seconds
}
Read Variables with Get
Finally, we need to retrieve the saved variables, for which we will use the Get() function. Of course, we will need to know the address where the variable is stored, as well as its type.
The following example reads a float variable and an example structure, which we must have previously saved in EEPROM memory.
#include <EEPROM.h>
struct MyStruct{
float field1;
byte field2;
char name[10];
};
void setup(){
float f;
int eeAddress = 0; //EEPROM address to start reading from
EEPROM.get( eeAddress, f );
Serial.print( "Float read: " );
Serial.println( f, 3 );
eeAddress += sizeof(float);
MyStruct customVar;
EEPROM.get( eeAddress, customVar );
Serial.println( "Structure read: " );
Serial.println( customVar.field1 );
Serial.println( customVar.field2 );
Serial.println( customVar.name );
}
void loop()
{
}
Download the Code
All the code from this post is available for download on Github.

