What happens when an Arduino or other microcontroller runs out of random access memory (RAM)? Typically the Arduino goes crazy and resets itself in strange places. To make matters worse these resets occur at strange places often sending you down a rabbit hole, looking for bugs that never existed in the first place.
What is Memory?
Microcontrollers have multiple forms of memory but the two main ones are Flash and RAM.
Flash stores your program. The instructions you typed into the Arduino IDE, translated to a language the micro can understand. Flash memory is non-volatile, meaning it persists through a power cycle. An Arduino Uno has room for about 16,000 instructions. Enough for a pretty complex program.
RAM, on the other hand, stores the data your program uses. Dynamic things such as variables, structures, the stack etc all go into RAM. It is volatile so each time the microcontroller is reset the RAM is cleared and repopulated. An Arduino Uno has 2,048 bytes of RAM. That’s not much, your laptop might have more than 4 billion bytes of RAM! So its pretty easy to run out on an Arduino. And you can’t just pop in another stick of RAM or insert a USB memory stick.
3 Methods for Reducing RAM Usage
The RAM available is usually significantly less than flash memory making it a precious resource for your Arduino project. Care needs to be taken to minimize its usage so you can maximize the functionality of your creation.
Its ok to use all of the flash memory available.
Its not ok to use all of the RAM. You’re program needs some working space to keep track of function calls and local variables (this is called the stack). If you’re using dynamic memory, some RAM will be used by a heap too.
The Arduino IDE helps by estimating the static ram required (variables etc) but it can’t know how the stack and heap will effect things. For example, what variable will come in and out of existence and when. If these dynamic variables fill up memory and start to overwrite the stack, then the program will crash. Crashes arising from memory corruption are often unpredictable and difficult to reproduce.
As a general rule, try to keep the RAM usage reported by the Arduino IDE to less than 60% of available RAM.
Remove Unused Variables and Check Your Types
Sometimes, when you are in the heat of programming, you are trying so many things that stray variables get created. These are variables which are declared but are no longer used. If they are larger objects containing structures and arrays these can chew up serious amounts of ram. The easiest way to find rogue variables is to comment them out and see if the compiler complains. If it does just un-comment as you are obviously still using it somewhere else in the program.
It’s also worth checking the types for the remaining variables. Are they suited to the task at hand? Using the correct types can save you a few bytes here or there. For example, if you were creating a counter that increments up to 100 then resets back to 0 again you clearly don’t need an unsigned integer capable of counting up to 65,535. A byte (or uint8_t) is much more appropriate as it uses 1 less byte of ram and easily covers the target range. Check out the table below for the number of bytes consumed by each data type on an Arduino Uno.
|boolean, bool||1||true or false (1 or 0)|
|char||1||Usually used to store an ASCII character|
|byte, uint8_t||1||Unsigned value between 0 and 255|
|int, short, int16_t||2||Signed value covering the range -32,768 to 32,767|
|unsigned int, uint16_t||2||Unsigned value ranging from 0 to 65,535|
|long, int32_t||4||Signed value ranging from -2,147,483,648 to 2,147,483,647|
|unsigned long, uint32_t||4||Unsigned value ranging from 0 to 4,294,967,295|
|float, double||4||Floating point value ranging from -3.4028235E+38 and 3.4028235E+38|
Store Static Strings in Flash Memory
Static strings can be one of the largest wastes of memory. For example:
These kind of static strings are automatically copied from FLASH to RAM when your program starts. Even if your program never changes them.
With the Arduino platform you can use a simple macro:
F(). This forces the string to be read directly from program memory (FLASH) when it is needed, and saves wasting RAM with a copy. This is done by surrounding your string with F(). For example:
Saving you 12 bytes of RAM, it all adds up!
Reduce Buffer Sizes
It’s easy to create a buffer with a nice round number size, say 50 or 100, or to load a library which takes care of everything behind the scene. This is fine when you’re getting started and you don’t yet know the exact structure of your data and you haven’t yet run into RAM shortages. Once it does start to run out you should try to make the buffers only as big as they need to be and dive into your libraries to check that their buffers make sense for your application as well.
For example, by default our serial command handler library allocates a 30 byte buffer for receiving messages, a 70 byte buffer for mapping variables to names and a 40 byte buffer for mapping functions to names. This is a really useful library for handling serial commands but it uses more than 140 bytes of RAM.
Fortunately, the command handler library is implemented as a template. You can pass parameters to control how much memory it uses:
template class CommandHandler<int MAX_COMMANDS = 10, int CP_SERIAL_BUFFER_SIZE = 30, int MAX_VARIABLES=10>.
If you create your command handler with no parameters, it will use the default values:
If you only need 4 commands, they are all less than 10 characters each, and you aren’t using any named variables you can save 122 bytes of RAM if you declare the command handler like this:
CommandHandler<4, 10, 0> MyCommandHandler;.
One buffer everyone generally uses is the serial receive buffer. This is filled by the microcontrollers UART. Its not usually a good idea to edit the system libraries (you have to do it every time you upgrade) but if your application doesn’t require a big serial buffer you can reduce its size to save a few extra bytes of ram. The serial buffer can be adjusted by editing the
HardwareSerial.h file usually found in in your Arduino IDE directory.
The bit you want to edit is called
A value of 16 or 32 should be safe to try.