Memory optimisation

I'm a beginner in Arduino coding. My first (mildly) complex sketch ran perfectly on a MEGA 2560 but kept crashing on a UNO. Until I totality rewrote the skeleton of my sketch (but leaving the same computations within it).

With the default framework of Arduino 0022, it seems more efficient (memory-wise) to call multiple small functions than to nest if and switch statements in a gigantic "main" function.

Is my assumption correct ?

Remember that the Mega has more RAM and more program memory than the UNO, though I think the RAM is the suspect here.
However, without seeing anything (hint), I'm simply guessing.

In case you run into memory issues,

On the efficiency in function calling:

  1. Stack space is filled with return address and parameters you pass to your function
  2. Your function may need to establish new variables costing more memory
  3. You can rewrite the layers of functions, combining some layers if the lower layer doesn't ever get called by more than one caller.
  4. Using global variables reduces parameters passing to your functions, but also reduces readability re-usability of your code. Say you have one global msg[] for printing messages for serial or lcd and any function uses that array.

Other issues:

  1. do you have too many strings? (move them to FLASH with PROGMEM)
  2. have you defined too many variables that don't ever change? (replace with #define)
  3. are you using float where a short will suffice? (reduce footprint of each variable)
  4. are you using too many overloaded versions of one library function? Say you use lcd.print(int) and lcd.print(string) and lcd.print(float). Try use only lcd.print(string) and use sprintf for formatting since it's the best way ever.

AWOL : I also think RAM is the problem, hence my question.
Here is my full source (working on the MEGA, hanging on the UNO) if it may help getting an idea : AstroCom_0_0605M.rar (7ko)

liudr : On the efficiency in function calling, I totally agree with you, but for some reason, it seems more efficient to split my main function ( MenuUpdate ) into many small functions, so my findings on this topic are kind of counter-intuitive.

In the following versions I tried to minimize Strings to a bare minimum, replaced every constant I could with #define, use only integer math, but it didn't work on the UNO until I split the main function to a few smaller ones (like in AstroCom_0_0607.rar).

I will try to reduce the number of overloaded versions of the function I use, I didn't even knew that Arduino Processing supported sprintf, thank you for pointing me that way.

I can't see any obvious large arrays, so maybe it is your strings.
Does it only fail when you enable DEBUG?
Try PROGMEM for your constant strings.

Edit: You use String a lot - I don't know how good this class is at garbage collection, but if that were the problem, it would also fail (eventually) on the Mega. Tricky.

AstroCom_0_0605M always fails on the UNO, but works on the MEGA.

I use Strings a lot but may replace it with char*, because in fact I don't use much of String's methods. One thing is sure, it doesn't leak memory because when running on the MEGA the memory usage is stable (tested with MemoryFree.h) from one loop to another and (but I know it may be irrelevant) it already ran an entire night without crashing.

This (older) version craches if I enable the DEBUGTEST : AstroCom_0_0502k.rar

pixelk,

As long as you are not nesting the small functions, ie. calling a from b and b from c and so on, you're fine on the stack. If you find a layer of function that is only called by one function above, absorb it into the other function.

You need stdio.h for sprintf. You can include (with some tricks I don't remember) the float output on sprintf but that costs bytes. I typically do it myself with two integers.

As for your code, like AWOL said, use PROGMEM for strings and also for those byte arrays. Also consider rewriting your EEPROM function to see if you can do some loops instead. Also if you can afford not to do Serial, lose all Serial commands. They cost you both FLASH and SRAM. I can see you're using lcd so maybe you can just use the lcd. You could have used my Phi-1 shield though :slight_smile:

FYI, I have a 2,700 line project that took 27K FLASH and I managed to create a static 800 Byte data storage in SRAM and still run the codes fine. There's lots you can do on your own program.

The failsafe thing to do here is debug your actual memory usage. My recommendation is to rebuild your program from scratch, slowly adding one piece at a time, and testing each one. Check the memory at each stage. This way you can see objectively what's eating up your memory. Also you can call it at various stages in your call stack to see if excessive nesting depth is causing the problem, although that's pretty rare in my experience. Usually the problem is strings or libraries.

This has the added benefit in that code ALWAYS seems to get better after a re-write from scratch, because little problems will become more obvious as we tackle one little piece at a time.

Here is the function I use to check memory. There is one subtlety... If the 'free' memory reports some ridiculously large number, that's actually negative and it means you're out of memory. Also if the Heap is 0 or ridiculously large, that's a problem too.

void command_memorycheck()
{
  void* HP = malloc(4);
  if (HP)
    free (HP);
    
  unsigned long free = (unsigned long)SP - (unsigned long)HP;
  
  Serial.print("Heap=");
  Serial.println((unsigned long)HP,HEX);
  Serial.print("Stack=");
  Serial.println((unsigned long)SP,HEX);
  Serial.print("Free=");
  Serial.println((unsigned long)free,HEX);
}

liudr : Thank you very much, your help is enlightening. Too bad I didn't know about your shield, I bought a DFRobot Keypad Shield on ebay a while ago for debugging purpose, but only because it was one of the first I found. In the final project I need a custom LCD (red on black) and a separate "keyboard", and this part is also already done and running.
Again, thank you so much for your help, real food for thought.

maniacbug : I already started from scratch 4 or 5 times, and I already use the MemoryFree library, thank you.

Hey you're welcome pixelk.

I'm getting my revised Phi-2 shield in a couple of weeks. Maybe if you're interested, check back or tell a friend :wink:

Just curious, are you going to use a number pad like this?
http://cgi.ebay.com/12-Key-Membrane-Switch-Keypad-Keyboard-General-Use-x-1-/260581851482?pt=LH_DefaultDomain_0&hash=item3cabe3815a

I sure will watch you work closely liudr.

I designed the "keyboard myself, with 6 buttons and a bunch of resistors.

The project actually look like this ( I'm just waiting to buy a anti-moisture spray - I don't know the exact english term - and a proper case ) :

Like the Arduino, it's a modular design, I can change LCDs, keyboard and - missing in this picture - add additional sensors.
The main board is a Sparkfun protoshield, but if I had to do it again, I'll probably go for a real cheap PCB (like the iTead Studio ones).

I only use DFRobot's keypad shield when debugging my source, like your Phi shields, it's really handy and a great way to kick-start a project.

Great looking project!

BTW, I'm pre-releasing my phi-menu, an open-source software core to implement user interactions and menus. Maybe you can take pieces from it.

Here's an example: I made a password input panel with the code.

http://arduino.cc/forum/index.php/topic,54823.0.html

BTW, I don't know if you know this: if you used an 80-wire hard drive cable, you'll be messed up! Only 40-wire hard drive cables have straight 1-to-1 connections.

http://arduino.cc/forum/index.php/topic,54426.0.html

It's a a floppy cable, which has the advantage to just have the right amount of connectors for a LCD connection.
If I remember well, the IDE cable went from 40 to 80 wire when they upgraded to UDMA-66. one conductor out of two is grounded, to help reducing EMI at the high speed attained by UDMA-66.

Yeah, that's about the time I started seeing fancy cables in Dell computers with handles and such. To be worse as general purpose cables, many of the 40 pins are connected together and to the 40 gnd wires so wiring is very tricky.

Like this one I did sometime ago:

Now I just use my shield for LCDs.