How to optimize your Arduino memory usage

If you are unfortunately hitting the memory limit on your arduino, let me first take the moment to congratulate you! You have already become a master of arduino and programming. Otherwise you would not have come up with so many lines of code to fill your arduino up!

Go through the typical practice I go through and trim some fat from your program so you will squeeze more onto your arduino.

First, you should know what content goes to what memory: program and variable initial values go to Flash, variables and their initial values go to SRAM. Yes, initial values go in both places. Read this if you want more understanding on what ram is what.

Now if Arduino IDE tells you your sketch is too long, it is complaining your sketch is bigger than the Flash. You should do the following things to reduce that usage:

1) Have you been using function calls instead of repeating your codes?

Eg. repeating code to blink LED:

void setup()

{}

void loop()

{

digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);

digitalWrite(2,HIGH);
delay(300);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(300);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(300);
digitalWrite(2,LOW);
delay(25);

digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);

}

This is very long and detailed. It offers no hint of what you’re doing but instead tells you all the details you don’t want to know and takes too much space. You should do the following:

Define a function to do the blink and call the function every time you want to blink:

void blink_long()

{

digitalWrite(2,HIGH); // This blinks the LED for 300ms
delay(300);
digitalWrite(2,LOW);
delay(25);
}
void blink_short()// This blinks the LED for 100ms
{
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
delay(25);
}

void setup()
{}

void loop()
{
blink_short();
blink_short();
blink_short();

blink_long();
blink_long();
blink_long();

blink_short();
blink_short();
blink_short();

}
Aha! You were sending the Morse code of SOS for help! Now it is super clear. You have also saved precious memory on your arduino Flash. From my test with Duemilanove, compile size is 1136 bytes for the long and tedious version but only 896 bytes for the short and tidy version. Have in mind that the 448 bytes were used as overhead. The actual size of your sketch’s function is 688 bytes for the long version and 448 bytes for the short version. This doesn’t sound like much but version long costs you 76 bytes memory per beep while version short costs you 50 bytes memory per beep. This all adds up. Just think about more complicated functions.

2) Have you been too verbose in your messages?

Say you do Serial.print(“Haha, you know what, the resistance is just as we expected, 100 Ohm, right on my man!”);

You should be doing this instead Serial.print(resistance);

Output the bare essential to not waste your storage space on arduino.

Also, if you have several places where you output essentially the same messages, make them exactly same. Arduino IDE will optimize and only keep one copy of the message so you save Flash.

Eg.

Serial.print(“This egg is bad, it is green!”);

Serial.print(“This egg is also bad, it is pretty green!”);

Serial.print(“This is a bad egg waiter, it is green!”);

You should output “Bad egg! It’s green!” instead so similar messages don’t get stored in three different ways on the Flash.

3) Have you been using all the compiled functions?

Say you have Serial.begin() in you setup and a few Serial.print() functions spread out in your code, initially for debugging. Now you’re fully confident with your code, you should remove them or comment them out. Otherwise if a function appears in your code, it will be stored in the Flash. If you shed the libraries you don’t need, you will save considerable amount of space. Arduino IDE does a good job on this but you can always help by removing junks.

4) Have you avoided function overloads? This is an advanced topic and should only be attempted by the experienced.

Say you want to print a few numbers to your serial monitor, you do this;

int a=10;
float b=1.57;
void setup()
{
Serial.begin(115200);
}

void loop()
{
Serial.print(a);
Serial.print(b);
}
Compiled size is 3570 bytes.

Instead you should have never overloaded the Serial.print(float) function and stick to Serial.print(int) instead:

Serial.print(a);
Serial.print(int(b));
Serial.print(int(100*(b-int(b))));

Compiled size is only 3064 bytes. Although we increased the complexity of the code, we have avoided to overload the Serial.print(float), saving 500 bytes. In larger programs, you will always use Serial.print(char[]) method and possibly sprintf() to organize your output. In this case, stop using Serial.print(float) or even Serial.print(int) just because you’re lazy. Always use sprint() and Serial.print(char[]) so you save from not overloading two functions.

Now if you want to optimize your SRAM, you should watch out the above plus the following:

1) Have you used the appropriate variable types for the job?

If your variable can be stored in a short, don’t use long. If it is not float, use int. This is especially useful if you have arrays.

You should also think about your measurements. Say you want to store voltages from an analog input, you used float array to store 2.503V etc. Wrong! You should have used an int array to store the original reading, which is only between 0 and 1024. You save 50% space just by doing this. You can convert them later if you want.

2) Shorten your char [] sizes to appropriate sizes.

If you are using a 16X2 LCD, define char msg[17];

You don’t need too much more than 16 characters as message buffer.

3) Have you used too many string literals? This is again for advanced programmers.

If you have many strings in your program, you should use PROGMEM to store them in the Flash and access them only when needed. I have a whole other topic on this issue so it’s not detailed here.

In retrospect, I have regretted taking my above suggestions a couple times so you want to know about this too. Aggressively reducing variable size can lead to overflow and memory corrupt. Make sure you’re not giving too little space for your variables and plan for future expansion.

17 Responses to How to optimize your Arduino memory usage

  1. Pingback: Optimizing your program « Liudr's Blog

  2. Rodrigo says:

    very useful! Thanks

  3. Pingback: Organize your code – big ASCII art font « Liudr's Blog

  4. ramo102 says:

    Hi Liudr,

    I tried to follow the point 4 from your suggestions (use Serial.println(char[]) only), but with no luck.

    This is my sample sketch:

    int a = 234;
    float b = 99.44;
    double d = 10.231;

    void setup()
    {
    Serial.begin(115200);

    // Serial.println(a);
    // Serial.println(b);
    // Serial.println(d);

    int len = sizeof(int) + sizeof(float) + sizeof(double) + 1;
    char res[len];
    sprintf(res, “%d\n%g\n%g”, a, b, d);
    Serial.println(res);
    }

    void loop() {}

    The binary sketch size is 3398 bytes using Serial.println(), while it is 3552 bytes using sprintf().
    I’m using the 022 version of the Arduino IDE.

    Am I missing something?

    Thanks in advance.

    • liudr says:

      You should serial.print this len you defined as “int len = sizeof(int) + sizeof(float) + sizeof(double) + 1;”.

      See what number you get and why this way was wrong.

      What do you mean by using Serial.println() vs. using sprintf? They should both be in your code to make it work. Did you mean serial.println(a) then b, then c, then d vs your posted code?

      BTW, from your “len” mistake I suspect you’re not advanced in your programming. Have you read the second line in my point 4? “This is an advanced topic and should only be attempted by the experienced.” I can only discuss it if you know what function overloading is.

  5. Righty says:

    Am I missing something here? Why make two functions for the same task?

    void blink_led(int longDelay, int shortDelay)
    {
    digitalWrite(2,HIGH); // This blinks the LED for 300ms
    delay(longDelay);
    digitalWrite(2,LOW);
    delay(shortDelay);
    }

    //Example use
    blink_led(300,25); //Blink the led on a long delay
    blink_led(100,25); //Blink the led on a short delay

  6. Pingback: The IR Shield

  7. bobemoe says:

    Hi

    I’m at the point now of having to optimise because for running out of both memory types. Some good tips, thanks.

    I can see no difference in your before and after code for point 4 (overloaded functions) altho I think I get what you mean.

    • liudr says:

      Hi,

      You are welcome! The overloaded function tip only applies to the case where you already use different versions of one function and later cut down to only one version. If you don’t see improvement, you’re not already wasting your memory with function overloads so no worries :)

      • bobemoe says:

        Hi again,

        What I meant was that I can see no difference in the example code you provided, you don’t appear to be using print(float) in your example?? Implementing this optimisation has improved my code :)

        Another big saver was getting my head around chr arrays and finally removing the need to include string.h for the easy String functions.

        What with that and your tips I’ve shaved 3.4K off my program now! yay \o/

      • liudr says:

        Aha! Fixed. I just did some silly copy and paste after I made the test. Now the codes are different. I agree with you. String class consumes quite a bit of resources. Without String class, you may want to know about the sprintf and sscanf functions.

  8. Leon Botha says:

    Another suggestion is to write and use your own external assembly written libraries, thereby saving HUGE chunks of compiler generated code. This is especially true for function calls and casting issues.
    For example:
    Consider the following ways to get the unixTime of the date
    1) Method 1 most expensive
    RTC.now().unixtime; //Most expensive call
    2) Method 2 less expensive
    Datetime date= RTC.now();
    int unixDate= date.unixtime;

    Not only do the results contradict what you would expect by the different ways to get the unixtime but both of these ways generate over 300bytes(!!) of code to get a 2-nd level int. If a global variable is declared for the Date then it rises up to 600bytes! This is ridiculous.
    A call in custom written assembly would be able to do the above in only 30 bytes or less, then you need the 2 bytes to store the value. The big problem is the overhead of switching/calling the custom assembly code but this can be overcome by increasing the ratio of code in the assembly function vs the overhead. Either way, the compiler generated code is extremely bulky and if you really need to squeeze out more program space then assembly can help.

  9. Pingback: Walkin’ the walk… | The Pendaqueous Sensor Pod

  10. Pingback: Leaner meaner code for the Pearl. | An Arduino Based Underwater Sensor Pod

  11. Nelson says:

    Hi, first of all thanks a lot for all these informations on how to make Arduino code smaller.
    By the way, the morse code for SOS is . . . – - – . . . (short, short, short, long,long,long, short,short,short) – see: http://en.wikipedia.org/wiki/SOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 51 other followers

%d bloggers like this: