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.
#includePROGMEM 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!