Subscribe via RSS
17Feb/124

Persistent Data on the Arduino (EEPROM)

It's taken me a year to realise that you can actually store data at runtime on the Arduino and happily turn it off, expecting the data to still be there when you turn it on. By this, I don't mean the code you've uploaded; I mean the actual values you've created/calculated/determined whilst your code has been executing on the Arduino.

Actually, I lie... it hasn't taken a year to 'realise'... it's taken a year to actually need the ability to store information. It occurred to me, whilst looking at Don's OpenLCB railstars products, that they'd need to store everything they 'learn' as you set them up with controller nodes. All of my previous projects would've forgotten all settings once you disconnect the power!

Memory Types on the Arduino

After a little research, it turns out that Arduinos have three types of memory areas. These would be the flash, EEPROM and SRAM. The former is the location that all 'sketches' and other compiled program code go, therefore being the largest area. The EEPROM is, depending on your chip, an area around 1k to 4k in size for storing data to be persisted. Finally the SRAM is the 'running' area where data is stored during runtime of your code.

Memory Type ATMega168 ATMega328P ATmega1280 ATmega2560
Flash 16k 32k 128k 256k
SRAM 1k 2k 8k 8k
EEPROM 512 bytes 1k 4k 4k

So, as you can see, the more you pay for the microprocessor, the more space you get to play with. I have used the Arduino Mega 1280 for a while and had never used the space available in the EEPROM... what a waste. Now I'm tinkering with the Atmega328P and, as it shows, there's a lot less space available to play with. Fortunately, depending on how frugal you are with data storage, there's more than enough for creating our OpenLCB nodes.

Working with the EEPROM

Arduino 1.0 (and all previous versions) include the EEPROM Library. This library includes two calls, being read() and write(). For the Atmega328P, I'm able to store a byte in 1024 areas. This expands to 4096 areas for the Mega.

By the way, for time-critical apps, an EEPROM write takes 3.3 ms to complete.

NOTE: As the Arduino page warns, EEPROMs are only good for 100000 writes! Please only write/update your EEPROM areas sparingly and when absolutely required.

Efficient storage of Bits/Bytes

Depending on your requirements, you may want to be more efficient in the way you store certain values. We'll start with booleans: if you're lazy and wont need to store over 1024 booleans on an Atmega328p then you can simply check the boolean and store a '1' or '0' in any of the 1024 areas. Of course, if you need more, then you'd want to efficiently use the 8 bits per byte that you have available. As each of those 8 bits can be a '1' or a '0', you can then actually store 8 booleans in each byte. It's simply a matter of 'or'ing 8 booleans together and left-shifting to ensure you set the correct bit.

byte setBit(store, bit) { //bit 1 is right-most
      store |= (1 << (bit - 1)); //set bit 5 to '1'.
}

byte clearBit(store, bit) {
      store &= !(1 << (bit - 1));
}

bool getBit(store, bit) {
      byte b = (1 << (bit - 1));
      return (store & b);
}

Arduino has a good bit of information on BitMasks and BitMath for those interested.

Using PROGMEM to store 'known' data

So, as previously mentioned, the Flash area has the most space available. The Arduino comes with the PROGMEM library for storing variables in this area. Note that you cannot easily write to this at run-time (I haven't dug far enough to work out if you really can) ... the goal is to just store large data in the flash and use it from there at runtime rather than copying to your limited SRAM first.

Firstly, you need to select from a datatype below:

Data Type Description
prog_char a signed char (1 byte) -127 to 128
prog_uchar an unsigned char (1 byte) 0 to 255
prog_int16_t a signed int (2 bytes) -32,767 to 32,768
prog_uint16_t an unsigned int (2 bytes) 0 to 65,535
prog_int32_t a signed long (4 bytes) -2,147,483,648 to * 2,147,483,647.
prog_uint32_t an unsigned long (4 bytes) 0 to 4,294,967,295

Now, declare it in the PROGMEM 'space'. It seems that the Arduino devs recommended it to be stored as an array as you'd usually only use this space for large amount of data.
I've chosen the prog_uint16_t (note that this var size is a 'word'), the code below stores two of these values and then uses them during execution.

#include 
PROGMEM prog_uint16_t myValues[] = { 123, 456 };

int k; //counter? don't quite know what for.
int readValue1, readValue2;
void setup() {
      k = 0; //read the first word. 
      readValue1 = pgm_read_word_near(charSet + k);
      k = 1;
      readValue2 = pgm_read_word_near(charSet + k);
}

void loop() {
      //now you should probably do something with these values...
}

And that's it.. I hope this helps some of you to limit your SRAM requirements and also to store data for users each time your device is switched off!

Comments (4) Trackbacks (0)
  1. FWIW, we’re—by which I mean the OpenLCB group—is developing a standard scheme for storing stuff in Arduino EEPROM. Every node has a permanently assigned ID, which is 6 bytes long, and is stored in the last 6 bytes of EEPROM, by convention. Not that you have to do it that way, but it seemed easiest for us. I’ve been using the first several hundred bytes of EEPROM to store EventIDs for producers and consumers, something that must also be remember across reboots. EEPROM is a surprisingly handy thing, and we’re lucky to have it! The NXP Arm Cortex MCUs I’ve been playing with lately don’t include any on-chip EEPROM!!

    • Sounds complex :) But not impossible.
      I’m quite looking forward into reviewing the code for OpenLCB. I’ll probably not get a chance to do so until my CAN ICs arrive though.
      I’m also more than happy to stick to the OpenLCB recommendations when creating anything OpenLCB-Compatible!

  2. Writing to program space (flash) is possible, but subject to certain restrictions. Atmel has a document about the topic: http://www.atmel.com/Images/doc2575.pdf

    Also, don’t forget that the flash can sustain far less erase/write cycles than the EEPROM. Rewriting the same page (or cell) many times will reduce part lifetime.

    If you need more permanent storage than the uC offers, why not attach some? It’s quite simple with serial EEPROMs. Have a look here for some Arduino specific instructions: http://www.arduino.cc/playground/code/I2CEEPROM

    Doing it without Arduino libraries is just as easy. :)

  3. I need to keep a slow counting total (amount of litres flowed) in memory in case of a power cycle. I do not want to lose the count which is between 1 and 50. when it reaches 50, I want to manually reset the counter. The count occurs over weeks of time, so it is possible that the power could be lost and restored to the unit (arduino uno). I downloaded the free code from “jaycar” website to perform the operation. Can I retain the count? Will it require constant read writing with each count? If so, how fast wil the eeprom degrade? Any help is appreciated.


Leave a comment


*

No trackbacks yet.