Arduino development with command-line tools


You may find that after your Arduino program grows to be over 1000 lines of C code that the Arduino IDE becomes increasing more annoying to use. Well, you can use TextMate, but first you need to set yourself up for command line programming. There are some instructions on the Arduino site for this, but it is a bit out of date. Specifically, the Makefile that they recommend simply does not work.

  1. Get the latest version of the Arduino IDE. This will include all of the avr-* command line tools that you’ll need to compile and upload your program (aka sketches) to your Arduino board. Make a copy and use the copy for your development. This way your command line experiments wont interfere with running the Arduino IDE.
  2. Copy the Makefile that is part of the install to the directory where you’ve your .pde source code. On the Mac, the Makefile is in: .../arduino-0016/hardware/cores/arduino/, 0016 is the latest version at this point.
  3. Edit the Makefile and follow the instructions. Basically define the required 7 environment variables. For example, my project is called “modem” and I use the Arduino Mini Pro 328, 8MHz system.
    TARGET = modem
    INSTALL_DIR = /Users/sjavey/Documents/arduino-0016
    PORT = /dev/tty.usbserial-A8007UEL
    UPLOAD_RATE = 19200
    AVRDUDE_PROGRAMMER = stk500v1
    MCU = atmega328p
    F_CPU = 8000000
    
  4. Now run the make command and see if you can build the applet_files target. You should see an output similar to this:
    $ make applet_files
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/pins_arduino.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_analog.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_digital.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_pulse.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_shift.d: No such file or directory
    Makefile:242: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WInterrupts.d: No such file or directory
    Makefile:243: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/HardwareSerial.d: No such file or directory
    Makefile:243: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WMath.d: No such file or directory
    Makefile:243: /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/Print.d: No such file or directory
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-g++ -M -mmcu=atmega328p -I. -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/Print.cpp | sed "s;Print.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/Print.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/Print.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/Print.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-g++ -M -mmcu=atmega328p -I. -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WMath.cpp | sed "s;WMath.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WMath.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WMath.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WMath.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-g++ -M -mmcu=atmega328p -I. -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/HardwareSerial.cpp | sed "s;HardwareSerial.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/HardwareSerial.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/HardwareSerial.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/HardwareSerial.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WInterrupts.c | sed "s;WInterrupts.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WInterrupts.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WInterrupts.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/WInterrupts.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_shift.c | sed "s;wiring_shift.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_shift.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_shift.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_shift.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_pulse.c | sed "s;wiring_pulse.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_pulse.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_pulse.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_pulse.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_digital.c | sed "s;wiring_digital.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_digital.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_digital.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_digital.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_analog.c | sed "s;wiring_analog.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_analog.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_analog.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring_analog.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring.c | sed "s;wiring.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/wiring.d
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -M -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/pins_arduino.c | sed "s;pins_arduino.o:;/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/pins_arduino.o /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/pins_arduino.d:;" > /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/pins_arduino.d
    # Here is the "preprocessing".
    # It creates a .cpp file based with the same name as the .pde file.
    # On top of the new .cpp file comes the WProgram.h header.
    # At the end there is a generic main() function attached.
    # Then the .cpp file will be compiled. Errors during compile will
    # refer to this new, automatically generated, file. 
    # Not the original .pde file you actually edit...
    test -d applet || mkdir applet
    echo '#include "WProgram.h"' > applet/modem.cpp
    cat modem.pde >> applet/modem.cpp
    cat /Users/sjavey/Documents/arduino-0016/hardware/cores/arduino/main.cxx >> applet/modem.cpp
    
  5. But now if you run the entire build, you’ll get this error: undefined reference to `__cxa_pure_virtual'

    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  -o applet/modem.elf applet/modem.cpp -L. applet/core.a -lm
    cc1plus: warning: command line option "-Wstrict-prototypes" is valid for C/ObjC but not for C++
    cc1plus: warning: command line option "-std=gnu99" is valid for C/ObjC but not for C++
    applet/core.a(Print.o):(.data+0x6): undefined reference to `__cxa_pure_virtual'
    make: *** [applet/modem.elf] Error 1
    

    The __cxa_pure_virtual is an error handler that is invoked when a pure virtual function is called. Somehow, this function is defined in some other library that is linked to the final program when you use the Arduino IDE, but it is not part of the Makefile. If you add a dummy definition for this function in your main .pde file, the makefile build will work. But I’m not sure if that is a good idea. For now, you can just add this definition to your main .pdf file and re-run the make.

    extern "C" void __cxa_pure_virtual() {}
    

    Now if you do a make clean followed by make, you’ll get an output that ends like this:

    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-gcc -mmcu=atmega328p -I. -gstabs -DF_CPU=8000000 -I/Users/sjavey/Documents/arduino-0016/hardware/cores/arduino -Os -Wall -Wstrict-prototypes -std=gnu99  -o applet/modem.elf applet/modem.cpp -L. applet/core.a -lm
    cc1plus: warning: command line option "-Wstrict-prototypes" is valid for C/ObjC but not for C++
    cc1plus: warning: command line option "-std=gnu99" is valid for C/ObjC but not for C++
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avr-objcopy -O ihex -R .eeprom applet/modem.elf applet/modem.hex
    
    
       text	   data	    bss	    dec	    hex	filename
          0	   4050	      0	   4050	    fd2	applet/modem.hex
    
    
  6. OK, so now you can compile from the command line, but can you upload it to the Arduino board? There is a make upload target, but when I run it, I get:

    $ make upload
    /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/bin/avrdude -V -F -C /Users/sjavey/Documents/arduino-0016/hardware/tools/avr/etc/avrdude.conf -p atmega328p -P /dev/tty.usbserial-A8007UEL -c stk500v1 -b 19200 -U flash:w:applet/modem.hex
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_recv(): programmer is not responding
    make: *** [upload] Error 1
    

    I did do a hardware reset, but it didn’t help. Anyone has got passed this point? I just saw this post, I’m not sure if it will help or not…

This is unrelated post is worth reading as it explains the build process in the IDE.

6 Responses to “Arduino development with command-line tools”

  1. Manuel Says:

    Hi,
    I hope you’ve solved this problem since you posted this. In case you haven’t, I ran into the same problem with my brand new arduino. You must perform a reset right before the upload. This can be done by sending a pulse by serial, from a python or perl script, and include it in the upload target of your Makefile. See http://johanneshoff.com/arduino-command-line.html .
    Cheers.

  2. Risto Mäki-Petäys Says:

    I’ve got a Duemilanove (atmega328 16 MHz), and I managed to fix the problem by replacing the upload speed to 57600 bps. Don’t know why that works, but I figured it out by arduino-0017/hardware/boards.txt which pointed out the right upload speed. However, uploading at 19200 bps didn’t work even if I reset the board quickly before uploading. Also I ended up removing -F and -V from avrdude options, since the signature check seems to work nowadays, and uploading with verification is still blazingly fast when compared to the Arduino IDE.

    Anyway, you’ll quickly end up in problems when you need external libraries. I’m developing a Makefile of my own as well, and I’m about to get the problem fixed soon. At the moment I still need to add the libraries manually to CXXSRC and run make twice. I also made a softclean target for a quick rebuild. It’s available at https://svn.kaverit.org/svn-pub/arduino/core/Makefile

    You don’t actually have to hardcode the target in the makefile. It’s enough if the pde file is under a directory with the same title (like Blink/Blink.pde)

  3. Philip Hands Says:

    As you can see I’ve been doing something similar, and have knocked up a Debian Source Package ready for collaborative development, so please feel free to join the project, or make suggestions.

    Cheers, Phil.

  4. Avis Bustamante Says:

    Really Good Articles/posts that you have on this site. Thanks

  5. Michael Says:

    You suggestion

    extern “C” void __cxa_pure_virtual() {}

    is very helpfull. Thanks a lot

  6. Mark Seaborn Says:

    The reason the Arduino IDE avoids the linker error about __cxa_pure_virtual when building some programs is that it compiles with the options “-ffunction-sections -fdata-sections” and links with “-Wl,–gc-sections”, which drops some references to unused symbols.

    So, you can try building using those options instead of providing your own definition of __cxa_pure_virtual.

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


%d bloggers like this: