PROGMEM is a keyword used when declaring a variable that keeps the data in flash instead of copying it into SRAM. It is part of the `pgmspace.h` library. Use this tag for discussions about using this attribute.
Memory types on an Arduino
RAM is precious on a microcontroller, PROGMEM (program memory) not quite as much so. On the Arduino Uno (using an Atmega328P) you have:
- 1 KB of EEPROM
- 2 KB of RAM
- 32 KB of PROGMEM (Flash Memory - where your program goes)
Clearly, there is a lot more program memory than RAM.
Constants are normally copied from PROGMEM into RAM
Constants in your program are initially stored in PROGMEM (where else could they be stored when the power is off?) but copied into RAM by compiler-generated initialization code at the very start of executing your sketch (program).
For example:
Serial.println ("Hello, world!");
The constant string "Hello, world!" is initially in PROGMEM, but copied into RAM at startup. This means that room for it has to be allowed for in RAM as well as in the Flash memory.
This uses up valuable RAM for what is really just a copy of something that will never change.
Another example:
const int NUMBER_OF_ELEMENTS = 10;
const float table[NUMBER_OF_ELEMENTS] =
{ 1.0, 34.234, 324.234, 23.1, 52.0, 3.6, 5.6, 42.42, 1908, 23.456 } ;
Although this table of floating-point values won't change, it is also copied into RAM. If you have a large table, you will soon run out of RAM.
The PROGMEM attribute
You can prevent this copying into RAM by adding PROGMEM
to a data declaration, like this:
const int NUMBER_OF_ELEMENTS = 10;
const float table[NUMBER_OF_ELEMENTS] PROGMEM =
{ 1.0, 34.234, 324.234, 23.1, 52.0, 3.6, 5.6, 42.42, 1908, 23.456 } ;
Accessing PROGMEM constants
Unfortunately the compiler (on the AVR Arduinos anyway) expects all data to be in RAM and does not allow for accessing data directly from PROGMEM.
You can make a small library PROGMEM_readAnything.h
:
#include <Arduino.h> // for type definitions
template <typename T> void PROGMEM_readAnything (const T * sce, T& dest)
{
memcpy_P (&dest, sce, sizeof (T));
}
template <typename T> T PROGMEM_getAnything (const T * sce)
{
static T temp;
memcpy_P (&temp, sce, sizeof (T));
return temp;
}
Copy the above into a text file, save it as PROGMEM_readAnything.h
, and place that into a folder called PROGMEM_readAnything
, and put that folder inside your Arduino libraries
folder which itself is inside your sketchbook folder. Like this:
(sketchbook location)/libraries/PROGMEM_readAnything/PROGMEM_readAnything.h
Now you can access a PROGMEM constant like this:
float thisOne;
PROGMEM_readAnything (&table[3], thisOne);
Or:
float thisOne = PROGMEM_getAnything (&table[3]);
The F() macro
For simple string constants, you can use the F()
macro to easily keep strings in PROGMEM, like this:
Serial.println (F("Hello, world!"));