Command-line Arduino development

When you first get started with Arduinos, it's generally best to use the Arduino Java IDE, since that's what all the tutorials use.

But after a while, if you're used to a programmer's editor, you start craving key bindings and global substitutes and all that good stuff. I confess I'm somewhat allergic to IDEs in general, and the Arduino IDE doesn't do anything to change that view.

Happily, there are tools available to let you do all your Arduino development from the command line. But they're a well-kept secret ... so here's how to use them.

Get the Java Arduino package

First, even if you're doing everything from the command line, you want to have the Java package from arduino.cc installed. It is possible to write Arduino programs without it ... but you'll struggle greatly figuring out how to do the simplest things, from delays to serial prints to math. The libraries you have to use have zero documentation. I did get a few programs working, but development took five times as long and it just wasn't worth the effort. Download the arduino.cc package and un-tar it somewhere. You won't be using the Java parts of it, just the libraries and header files.

Install the compiler and avrdude

The compiler for the Atmel chip and Arduino architecture, avr-gcc, typically comes in a package named gcc-avr. Or is it the other way around? It has some dependencies that aren't necessarily set up as dependencies, like avr-libc and binutils-avr.

You'll also need to download your file to the Arduino after you build it: the program that does this is avrdude.

So on Ubuntu or Debian, this should give you everything you need:

$ sudo apt-get install gcc-avr avr-libc binutils-avr avrdude

There's a gdb-avr package as well, though I haven't used it.

The Makefile should work on BSD too, but I'm told openbsd's avrlibc port is broken, so you may have to apply some fixes there first.

Get a Makefile

Okay, you have the compiler, libraries and avrdude. What now?

avr-gcc wants you to specify information about what type of Atmel chip you have. You don't want to type that in every time you compile ... so set it up in a Makefile so all you have to do is type make

Most of the Arduino Makefiles you'll find on the web are for ancient versions of the Arduino software like arduino-0011 (they're on -0022 now). So here's mine: Makefile-arduino-0.8. (If you're still using Arduino 0022 or 0023, you might want Makefile-0022-v4).

Name it just plain Makefile and put it in a directory with one of your projectname.ino sketches. Give the directory and the .ino file the same name, e.g. blink/blink.ino.

Read the instructions at the beginning of the Makefile to see what you'll need to change. You'll need to set your projectname (the same as the name of your .pde file) and the path to wherever you've put the Arduino-1.0 software.

You may need to change ARDUINO_MODEL, but I've tried to make it guess properly, so as of version 0.7 of the Makefile, you should be able to make download to either an Atmega328 or an Uno without editing the Makefile. As of version 0.8 it also handles ATTINY, works on Mac and maybe even on Windows, thanks to Bernard Pratz.

What does the Makefile do?

First, it makes it easier to keep track of all those variables like MCU and download speed.

It also lets you keep your sketch in a simple .ino file, so if someone who uses the IDE wants to try your sketch, it should work.

To do that, when you type make it creates an applet/ directory, and makes a file, projectname.cpp, where it adds some extra boilerplate you'll need: it includes the "WProgram.h" header. It also adds a main() function at the end of the file that calls init();, then setup();, then for (;;) loop();

Finally, it adds a magic incantation to avoid an error you'd otherwise get:

applet/core.a(Print.o):(.data+0x6): undefined reference to `__cxa_pure_virtual'

The magic incantation is this:

extern "C" void __cxa_pure_virtual() { while (1) ; }

Apparently the problem is that somewhere in the Arduino Print library, there's an object created with a pure virtual function, and that causes the compiler to demand an error handling function. (This may be because avr-gcc is only a partial C++ implementation and doesn't include pure virtual functions.) I haven't seen a satisfactory explanation of why it's an infinite loop, but that's how everyone recommends you define it. There's more discussion on Stack Overflow: What is the purpose of __cxa_pure_virtual? and Arduino forum: C++ problem with pure virtual method.

This problem has persisted at least from version 0018 to the current 0022, and nobody seems to care much about fixing problems that only show up in command-line compiles, so the Makefile adds it for you.

Compile, and download to the Arduino

Type make to compile. With any luck, that works.

Then plug in your Arduino, press the reset button if your model requires it, and type make download to download the program. That should be all you need to do.

Possible download issues

Sometimes I get an error indicating the Arduino is busy or I can't write to it (I can't remember the exact error message -- hasn't happened in a while.)

The Java software always seems able to reset the serial line, but I'm not sure how it does that. The Makefile has three possible ways to reset the device, and one may work better than the others for you. If you have trouble, try commenting out the RESET_DEVICE line and uncommenting one of the other two.

By default, it tries stty -F /dev/ttyUSB0 hupcl

In Perl, you need libdevice-serialport-perl:
perl -MDevice::SerialPort -e 'Device::SerialPort->new("/dev/ttyUSB0")->pulse_dtr_on(1000)'

Python version needs python-serial:
python -c "import serial; s = serial.SERIAL('/dev/ttyUSB0', 57600); s.setDTR(True); sleep(1); s.setDTR(False)"

Replace /dev/ttyUSB0 with the real device, as needed.

Of course, if none of these work, you can always unplug the Arduino and plug it back in. But that gets old after a while.

On some devices you may see this error when downloading:

Device signature = 0x000000
...
Yikes!  Invalid device signature.

This seems to be a common error, due to a bug in some bootloaders, but it doesn't seem to cause any real problems, so don't panic if you see it.

If you get an error opening the serial port, or if you have multiple USB serial devices plugged in, you might need to adjust the Makefile line:

PORT = /dev/ttyUSB*

Other options for Arduino development

Mac Xcode users may be interested in embedXcode, a set of templates that lets you do cross-platform embedded development (including Arduino) from the Xcode IDE. It may use some code from my Makefile under the hood for its Arduino component.


More Software
Shallowsky Home
mail me