💡 Electron Parade
← Back to Academy

Lesson 180: Memory Management & PROGMEM (Saving RAM)

Arduino RAM Concept

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.

Understanding the Arduino’s Brain

An ATmega328P microcontroller has three types of memory:

  1. Flash Memory (32KB): This is where your compiled code (the actual program instructions) is stored. It’s non-volatile (survives power loss).
  2. SRAM (2KB): Static Random Access Memory. This is where your variables, objects, and temporary data live while the program is running. It is volatile (lost on power loss).
  3. EEPROM (1KB): Used for long-term storage of small amounts of data (like configuration settings).

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.

The Hidden Danger of Serial.print()

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.

The F() Macro: The Easiest Fix

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.

PROGMEM: For Arrays and Data

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};

The Catch: Reading PROGMEM

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:

How 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.

Summary

  1. Keep an eye on the Arduino IDE console output when you compile. It tells you exactly how much “Dynamic memory” (SRAM) you are using. Keep it under 80% to leave room for local variables created during runtime.
  2. Wrap all static text in Serial.print() and lcd.print() with the F() macro.
  3. Use PROGMEM for large arrays of constant data.

Don’t let your project crash just because it forgot how to think!