How Does Memory Work On The Arduino, Anyways?

After reading an interesting article on memory (and having finished the printf code I posted yesterday), I realized that memory can be one of the more odd aspects of programming the Arduino. Take for example:

  • In most computers, a pointer is a pointer is a pointer – but on the Arduino, there are actually three locations: the eeprom, the program memory, and the RAM. Each can have the same pointer value, making things quite confusing!
  • Program memory is where everything starts; however, global values are normally copied over to RAM on startup, effectively doubling the space they use (one copy in program memory, one in RAM).
  • Your RAM gets chopped up quickly: the bottom is system variables and those globals, then the ‘heap’ grows up, while the ‘stack’ grows down. If the two meet, your program is likely over – whether you want it or not!

What to do then?

  • Watch globals. They take up space right from the start, and never let it go.
  • Minimize variables. Unsigned longs are twice the size of ints – use a lot of them and you’ll see your free space shrink twice as fast. Variables that never stray from 1 may be perfect for byte values, and so on.
  • Watch strings. They are ‘double dippers’ – first copy in the program, then a second copy in RAM. And while an int only takes 2 bytes, strings grow much bigger very easily.
  • Avoid recursion, or deep subroutine calls. Every subroutine call uses memory – and while it’s rarely a problem, if you use recursion, it can eat up space fast.
  • Keep info out of RAM. This page explains how to use PROGMEM to keep data in the program side of things, and not in the RAM. Since the ATMega328 has about 32k of program versus 2k of RAM space, it can be a real benefit.
  • Monitor the memory. Use the freemem() function and display it from time to time in your program, to get an idea of how much space you have left:
    #include <avr/pgmspace.h>
    int freeRam () {
      extern int __heap_start, *__brkval;
      int v;
      return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
    }

Of course, sometimes you just can’t save enough space; however, with an eye on the memory size, you can often be ready for when memory gets too close to the bone.

A Better Serial.print() For Arduino

In a previous article I described how to add the old-fashioned print() function to Arduino to improve debugging – after all, it gets tedious to use a separate Serial.print() function for each type – and inserting information into a string is printf’s specialty.

However, while I found they did the job, they weren’t quite what I wanted. for one thing, the memory they used for the format string is in RAM, which means it contributes to ‘eating up’ the 2k memory that the ATMega328 on the Arduino has to use. Any string does this, of course, but prints can eat up string space quickly, especially if you add a lot of debugging code.

Another problem when I mentioned the code to others seemed to be the waste of space for the buffer character array. However, this wasn’t a big issue, since the buffer only existed for the time the function was around – and in tight memory situations, it could be shrunk by editing the length.

Still another issue: I didn’t like the way it handled the Serial object, since it made it hard to reuse – for example, if I used a software serial device, or I programmed on the Mega (which has 4 hardware serial ports).

For these and other reasons, I decided to improve the code, and did a search on the Internet. The result is one function and two macros, which save memory while giving you more flexibility:

#include <avr/pgmspace.h>

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void StreamPrint_progmem(Print &out,PGM_P format,...)
{
  // program memory version of printf - copy of format string and result share a buffer
  // so as to avoid too much memory use
  char formatString[128], *ptr;
  strncpy_P( formatString, format, sizeof(formatString) ); // copy in from program mem
  // null terminate - leave last char since we might need it in worst case for result's \0
  formatString[ sizeof(formatString)-2 ]='\0';
  ptr=&formatString[ strlen(formatString)+1 ]; // our result buffer...
  va_list args;
  va_start (args,format);
  vsnprintf(ptr, sizeof(formatString)-1-strlen(formatString), formatString, args );
  va_end (args);
  formatString[ sizeof(formatString)-1 ]='\0';
  out.print(ptr);
}

#define Serialprint(format, ...) StreamPrint_progmem(Serial,PSTR(format),##__VA_ARGS__)
#define Streamprint(stream,format, ...) StreamPrint_progmem(stream,PSTR(format),##__VA_ARGS__)

void setup()
{
  Serial.begin(9600);
  Serial.print("12345678901234567890123456789012345678901234567890");
  Serialprint("");
  Serialprint(" memory available: %d bytes\n",freeRam());
}
void loop()
{
}

The relevant code here is the function StreamPrint_progmem(), and the macros Serialprint() and Streamprint(). In code, you’d only use the macros; so for instance you’d replace:

Serial.print("hiya!");

With

Serialprint("hiya!");

Notice you only have to remove the period – it’s the reason for the function name, to make it easy to switch over.

But of course it becomes really handy with those long entries:

Serial.print("Count: ");
Serial.print(count);
Serial.print(", Data: ");
Serial.print(data);

Can become (assuming both items are numbers):

Serialprint("Count %d, Data: %d",count,data);

And as a bonus, the strings do not go into the 2k the computer needs to run.

In fact, you can prove it – run the above sketch and note the size, which is the available memory of the 2k. then, swap the Serial.print() with the Serialprint() – and notice the size change by 50 bytes, the size of the string. And that’s 50 bytes you won’t have to look for as you program gets tighter and tighter!

There are some notes about this, however:

  • There is no println() version – instead, add \n or \r to your strings:
    Serialprint("Count %d, Data: %d\n",count,data);
  • The Streamprint() is the more flexible option – you can use it for other serial items, of even the NewSoftSerial object. You just pass the Serial object of your choice as the first parameter – for example, here we’ll use Serial, in effect, doing the same as Serialprint();
    Streamprint(Serial,"Count %d, Data: %d\n",count,data);
  • To save buffer space, I combined two buffers into one (temporary format string storage, and the print result). Because of this, the current size of 128 characters is a bit misleading – you’ll only have ‘about’ 64, since half will be the temporary format string, and half will be the final output. I felt this was better than using two larger buffers, and risking not enough room for either. If you find your text is getting truncated, just increase the buffer size (remember, it only exists while the function runs).

Oh, and by the way, the ‘Better’ in the title is subjective – I like it better. whether you’ll find it better is up to you decide – but I hope it comes in handy.

Using NVDA For The Blind On Windows

I’m not blind, but after reading Graham’s McCreath’s The Politics of Blindness I’ve been trying to be more aware of their problems with viewing data online. For example, I can’t figure out how a blind person makes sense of Windows – after all, many sighted people have problems!

I also can’t understand how blind people can afford to use the Internet or a computer – after all, screen reader programs like Jaws are in the thousands of dollars, and devices that ‘print’ online text in Braille form are likewise incredibly high priced for the average person – blind or not.

So I was pleased to notice while doing some research that there is one free solution – a Windows screen reader for the blind called NVDA, from the NVDA Project.

This program (which you can download from their site), is a real treat to play with – run it, install, and then move your mouse around the screen. Incredibly, it speaks back to your whatever you put the mouse over! It’s not perfect, but I found it very informative when I closed my eyes – which I guess is where it is especially useful.

The program resides as a tray icon, which you right click to stop when you’re done, or to play with settings. For example, I did find the default voice, while fun, could be improved. Using these settings, you can choose Preferences; Synthesizer; Synthesizer from the menu and select “Microsoft API version 5″ (in Windows 7, at least). The result is a much improved female voice. However, even if that’s not available, there is a huge selection of voices to change to – go to Preferences; Voice settings; Variant and choose ‘Mr.Serious’ for example.

Apparently, it also connects to Braille displays for tactile output of the text using brltty (a Linux braille reader device driver) – however, not having one, I couldn’t try that feature out.

So if you know someone who could benefit from this, please pass on the information and links – and of course, feel free to visit NVDA’s website and donate – I’m sure it will help!

How Much Can An Arduino REALLY Take?

When I was writing about speaker audio I reviewed the Arduino processor’s specs, particular section 28.1, “Absolute Maximum Ratings”. For example:

  • 6.0v for the chip
  • 40mA per I/O pin
  • Maximum total current of 200mA (“DC Current VCC and GND Pins”)

Sounds good – then I read the fine print:

Exposure to absolute maximum rating conditions for extended periods may affect device reliability.

So not only was exceeding these values bad, but staying at them for extended periods of time was bad as well!

So I now understood why many sites mentioned much lower values for each pin and total pins (20mA and 100mA, respectively), and also why we work with 5.0v on the Arduino – why go too close to the sun and pull an Icarus with our fragile CPU?

So from now on I’m aiming lower: 20mA per I/O pin, 100mA total, and you likely won’t hear of overclocking to 20mhz or pumping up the voltage anywhere on this blog.

I’ll be good – I promise.

Adding A Speaker To The Arduino – Carefully

So for my next trick, I wanted to see (hear?) what audio was like on the Arduino.

In theory, it’s nothing – connect the speaker to the Ground and pin 8, one wire each, and then run the example sketch ToneMelody (in the 2.Digital section), which plays out of pin 8 (note that the code runs once and stops – during testing, if you need constant audio, just switch the function names “setup” with “loop” in the code, and the audio will repeat tirelessly).

Of course, by now I was aware that simple isn’t always safe. Some examples show a resistor; some don’t. What to do?

Out came Ohm’s Law: since no pin could provide more than 40mA (maximum), I knew that 125 ohms was the minimum in this path (I don’t know anything about impedance, or how much actual current was passing through the speaker because of the waveform – I was playing it safe). I also knew my speaker was 8 ohms, so in theory I could use a 117ohm resistor and be safe. Of course, getting that close to the maximum was silly; after all, my resistors were 5% tolerance, which meant that my mythical 117ohm could be about 6ohms high or low. So I opted for a 1Kohm resistor for the first test, which put a ceiling on current of only 0.5mA [5v / (1000+8)ohm].

Quiet, but I heard it – a little tune:

Arduino speaker

I am truly grateful for alligator clips, as I was able to hook it up and get going in no time. Of course, now I knew it worked, it was time to lose the clips, so I quickly soldered two jumpers (actually one jumper split in two) to the ends of the speaker for easy use:

I also took the time to put in a lower resistor (180ohm) as well as a potentiometer I’d soldered more jumpers to. The result was a simple volume control: through the ‘pot’ (the extra yellow lead is not connected, just put there to get it out of the way), through the 180ohm resistor (so there is always a minimum safe resistance), then through the speaker and back to pin 8. Now I have s speaker to play with, and a volume control so as to not wake up the missus. Joy!

How To Draw A Simple Schematic For Free

If you’ve looked at my other posts, you’ll notice I’ve added schematics. While I won’t do them for every post, where possible (and needed) I’ll try to add them.

The tool I use by the way is a free one: ExpressPCB from the company of the same name. They offer it so you can design PC Boards, which they can then produce for you. However, the schematic half o f the program is perfectly usable even if you don’t want to do PC boards – as my examples showed.