
Youâve written a masterpiece. Your Arduino sketch is a glorious symphony of LCD menus, Wi-Fi web pages, and witty serial debug messages. You hit upload, it compiles successfully, and then⌠it crashes immediately. Or it reboots randomly. Or the LCD displays gibberish.
Welcome to the painful reality of running out of SRAM.
The standard Arduino Uno (ATmega328P) has a microscopic 2 Kilobytes (2048 bytes) of SRAM. Thatâs it. For comparison, a modern smartphone has about 4,000,000 times more memory.
If you arenât careful, it is incredibly easy to fill up 2KB, and when you do, your Arduino will fail in weird, unpredictable ways. Today, weâre going to learn how to monitor your memory and how to use the PROGMEM keyword to move your bulky data out of SRAM and into the much larger Flash memory.
An ATmega328P microcontroller has three types of memory:
When your program runs, it aggressively consumes SRAM. Every variable takes space. int takes 2 bytes. float takes 4 bytes.
But the biggest hidden memory hogs are strings of text.
Look at this innocent line of code:
Serial.println("System Initialized. Awaiting user input...");
That string of text is 44 characters long. In C++, every character is 1 byte, plus 1 hidden byte at the end (the null terminator). So, that one line of text consumes 45 bytes of your precious SRAM.
If you have dozens of Serial.print() statements, LCD menus, or HTML code for a web server, you will easily eat up all 2048 bytes just holding text!
Why? Because by default, the Arduino compiler assumes you might want to modify that text later, so it copies the text out of Flash memory (where the program lives) and into SRAM when the Arduino boots up.
If you are just printing text to the Serial monitor or an LCD, and you know that text will never change (itâs a constant), you can use the F() macro.
The F() macro tells the compiler, âHey, leave this string in Flash memory. When I need to print it, just read it directly from Flash one byte at a time. Donât waste my SRAM!â
Bad (Uses SRAM):
Serial.println("Starting the initialization sequence now.");
Good (Saves SRAM):
Serial.println(F("Starting the initialization sequence now."));
By simply wrapping your static text in F(), you can instantly free up huge amounts of SRAM.
The F() macro is great for simple strings, but what if you have a massive array of data?
For variables and arrays, we use the PROGMEM keyword (Program Memory). This forces the compiler to store the variable only in Flash memory.
First, include the library (usually included automatically, but good practice):
#include <avr/pgmspace.h>
Next, define your array with const and PROGMEM:
// This array normally takes 10 bytes of SRAM
// Now it takes 0 bytes of SRAM, and lives in Flash!
const int sensorCalibration[5] PROGMEM = {10, 250, 500, 750, 1000};
Because the data is no longer in normal SRAM, you cannot read it normally. If you try to do int myValue = sensorCalibration[2];, the Arduino will crash because itâs looking in SRAM, but the data isnât there!
You must use special functions to read from Flash memory:
pgm_read_byte() - for char or bytepgm_read_word() - for intpgm_read_float() - for floatHow to read it:
// Read the 3rd element (index 2) from Flash into SRAM temporarily
int myValue = pgm_read_word(&sensorCalibration[2]);
Notice the & (address of) operator. You have to tell the function exactly where in memory to look.
Serial.print() and lcd.print() with the F() macro.PROGMEM for large arrays of constant data.Donât let your project crash just because it forgot how to think!