We have earlier written a rather deep post about a non-volatile (not lost when power is) way to store data here, but in this post we will focus on the following question:
How can I save and load configuration data on my Arduino?
And this data should of course not be erased when the power is gone!
To solve this we use an often forgotten little feature on the microcontroller that resides on most Arduino boards (on the Arduino Uno we use here: ATMEGA328P-PU), namely EEPROM.
EEPROM stands for Electrically Erasable Programmable Read-Only Memory. This memory is non-volatile, which means that the data doesn’t get erased when the board loses power.
Implementation
We want to have a globally define configuration layout. We can solve this by defining a struct that holds the data. Here the configuration is only a lonely integer, but this is of course easy and possible to change:
typedef struct { char version[6]; // place to detect if settings actually are written int my_setting_integer; } configuration_type;
As you can see on line 3 we have written about a “version”. This is to detect if the data that resides in the EEPROM actually is valid configuration data!
If we cannot find valid data in the EEPROM we need to write and use default configuration settings instead.
We can define the config version, and fill it with default settings like this:
#define CONFIG_VERSION "VER01" // loaded with DEFAULT values! configuration_type CONFIGURATION = { CONFIG_VERSION, 42 };
And if we combine all this with some EEPROM magic that loads a configuration struct (if it has the correct CONFIG_VERSION) and save it back (overwrite) when we want, we have what we need!
#include <EEPROM.h> #define CONFIG_VERSION "VER01" // Where in EEPROM? #define CONFIG_START 32 typedef struct { char version[6]; // detect if setting actually are written int my_setting_integer; } configuration_type; // with DEFAULT values! configuration_type CONFIGURATION = { CONFIG_VERSION, 42 }; // load whats in EEPROM in to the local CONFIGURATION if it is a valid setting int loadConfig() { // is it correct? if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] && EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] && EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2] && EEPROM.read(CONFIG_START + 3) == CONFIG_VERSION[3] && EEPROM.read(CONFIG_START + 4) == CONFIG_VERSION[4]){ // load (overwrite) the local configuration struct for (unsigned int i=0; i<sizeof(CONFIGURATION); i++){ *((char*)&CONFIGURATION + i) = EEPROM.read(CONFIG_START + i); } return 1; // return 1 if config loaded } return 0; // return 0 if config NOT loaded } // save the CONFIGURATION in to EEPROM void saveConfig() { for (unsigned int i=0; i<sizeof(CONFIGURATION); i++) EEPROM.write(CONFIG_START + i, *((char*)&CONFIGURATION + i)); } void setup() { Serial.begin(9600); Serial.println("Hello world!"); // prints hello with ending line break if(loadConfig()){ Serial.println("Config loaded:"); Serial.println(CONFIGURATION.version); Serial.println(CONFIGURATION.my_setting_integer); }else{ Serial.println("Config not loaded!"); saveConfig(); // overwrite with the default settings } } void loop() { //every 5s increment and save the settings! delay(5000); CONFIGURATION.my_setting_integer++; saveConfig(); }
Now the program saves a new config every 5 seconds (remember that EEPROM has a max count on write cycles on ~100 000).
The output in the serial terminal looks like this when it is booted up with a valid configuration stored in the EEPROM, and then restarted after a minute:
Hello world! Config loaded: VER01 42 /////// RESTARTED /////// Hello world! Config loaded: VER01 54
You can of course edit and include what you need in the configuration_type
to make it suit your needs!