šŸ’” Electron Parade
← Back to Academy

Lesson 180: Memory Management & PROGMEM (Saving RAM)

Have you ever hit ā€œUpload,ā€ and watched your Arduino mysteriously freeze, restart, or print pure gibberish? If your code is fine but the board is acting possessed, you’ve hit the invisible wall: You ran out of SRAM. The Uno only has 2KB of RAM, which is laughably small. Today, we learn how to use PROGMEM to force your code into flash memory and save your projects from crashing.

The Three Types of Memory

Your Arduino Uno (specifically the ATmega328P chip) has three different pools of memory. Understanding them is the key to writing stable code.

  1. Flash Memory (32 KB): This is where your code (the sketch) is stored. It’s non-volatile, meaning it survives when you unplug the board. 32 Kilobytes might sound small, but it’s massive for compiled C++ instructions. You rarely run out of Flash.
  2. EEPROM (1 KB): A tiny storage space that also survives power loss. It’s used for saving small settings, like a high score or a calibration value. (We’ll cover this in a future lesson).
  3. SRAM (2 KB): Static Random Access Memory. This is where your sketch creates and stores temporary variables, arrays, and text strings while it’s running. It is volatile—unplug the board, and the SRAM is wiped clean.

The Problem: 2 KB (2,048 bytes) of SRAM is incredibly tiny. If you use a lot of Serial.print("Some long text here"); statements, arrays, or global variables, that 2KB fills up instantly. When it overflows, your program crashes.

The Quick Fix: The F() Macro

The biggest hidden SRAM killers are text strings. Look at this line of code:

Serial.println("System starting up, checking sensors...");

By default, the Arduino compiler takes this text string, stores it in Flash memory, and then copies it into SRAM when the board boots up. If you have dozens of Serial.print() menus or debug messages, your SRAM is gone before loop() even starts!

The solution is the F() macro. It tells the compiler to keep the string in Flash memory and read it directly from there, completely skipping the SRAM.

Before (Eats SRAM):

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing the primary sensor array...");
  Serial.println("Connection to Wi-Fi module successful.");
  Serial.println("Error 404: Actuator not found on I2C bus.");
}

After (Saves SRAM):

void setup() {
  Serial.begin(9600);
  Serial.println(F("Initializing the primary sensor array..."));
  Serial.println(F("Connection to Wi-Fi module successful."));
  Serial.println(F("Error 404: Actuator not found on I2C bus."));
}

Just wrap your double-quoted strings in F()! This simple trick can easily free up hundreds of bytes of SRAM.

The Advanced Fix: PROGMEM

The F() macro is fantastic for simple strings, but what if you have a massive array of data? For example, custom graphics for an OLED screen, wavetables for a synthesizer, or a giant lookup table?

You can’t use F() for arrays. Instead, you use the PROGMEM keyword.

PROGMEM tells the compiler: ā€œStore this variable in Flash memory, and do NOT copy it to SRAM.ā€

1. Including the Library

To use PROGMEM, you first need to include the AVR program memory library at the very top of your sketch:

#include <avr/pgmspace.h>

2. Storing Data in PROGMEM

Let’s say we have an array of integers representing a sine wave. We add const (because data in Flash can’t be changed while running) and PROGMEM.

// Store this large array directly in Flash memory
const int sineWave[10] PROGMEM = {0, 50, 100, 150, 200, 250, 200, 150, 100, 50};

3. Reading Data from PROGMEM

Because the data isn’t in normal SRAM, you can’t just read it like a normal variable (e.g., int x = sineWave[2]; will fail). You have to use special functions to fetch the data out of Flash memory.

For an integer array, we use pgm_read_word_near():

void setup() {
  Serial.begin(9600);
  
  // Read the 3rd value (index 2) from PROGMEM
  int myValue = pgm_read_word_near(&sineWave[2]);
  
  Serial.print(F("The value is: "));
  Serial.println(myValue);
}

Note: For byte arrays, use pgm_read_byte_near(). For floats, use pgm_read_float_near().

Summary

When your Arduino starts crashing mysteriously, check your SRAM usage! The IDE prints memory usage at the bottom of the screen every time you compile. If your ā€œGlobal variablesā€ use more than 75% of dynamic memory, you are in the danger zone.

  1. Always use the F() macro for static text inside Serial.print() and lcd.print().
  2. Use PROGMEM to lock large, unchanging arrays (like fonts, bitmaps, and lookup tables) inside the much larger Flash memory.

By mastering memory management, you can cram massive, complex programs into the tiny brain of the Arduino Uno without breaking a sweat!