For the projection screen controller I'm building I decided to use an Arduino hooked up to a GuruPlug. This way I offload the communication with the outside world to the GuruPlug, which runs a full Linux server, complete with Ethernet and WiFi connectivity. The Arduino is now used to control the motors and relays, and to receive input from an IR sensor, limit switches and infrared proximity sensors. This solution replaces the relatively cumbersome solution involving an Ethernet shield hooked up to the Arduino.. The software on the Arduino becomes a lot simpler too, without the Arduino TinyWebServer running on the Arduino board itself.
However it does have its own complications. Since the Arduino is now hooked up to a remote computer, I need to be able to build the C++ code for the Arduino on my host development machine (a MacOS X), push it to the GuruPlug running Linux and have the GuruPlug program the Arduino with the new binary.
The build process for the Arduino is handled internally by the Arduino IDE. The logic needed to build a typical Arduino project is hidden inside the Java program, which knows about the Arduino built-in libraries, as well as any additional user-defined libraries. I was hoping I could write a simple makefile to do the actual build, but the library dependencies are determined implicitly from the C header files included by your project's
.pde file. It could be done using a combination of shell and awk, and a recursive invocation of make, but it's slightly more complicated. A major problem I hit was with file names containing spaces, which abound at least on MacOS X. Make uses a lot of built-in functions that use space as delimiter between the elements of a list, and it's impossible to use any built-in make functions.
Because of the space character problem, I gave up on make. I was looking for a build system that uses a more programatic approach, and so I found SCons. It's a nice build system built entirely in Python. I was about to write a build file for it when I found arscons, a small build file for Arduino-based projects. The funny thing is that the project was checked in Google Code only two weeks ago. Very timely indeed!
In classic open-source fashion - which I haven't done since I joined Google, back in 2003 - I made few improvements to the code and sent them to the author. I can now build my Arduino project on MacOS X, have it pushed to the GuruPlug and from there uploaded to the Arduino board connected to it.
For this to work there are a few prerequisites for your development system. Make sure you have Python 2.5 or newer installed. Once you have Python installed, install SCons and the Python Serial module. On a Linux system you can install these by doing:
sudo apt-get install python2.5 python-serial scons
If you're on a Mac, you can get these slightly easier by installing MacPorts. If you do this, make sure you're running the python that comes with MacPorts, and not the default one in the MacOS X distribution. If you decide not to use MacPorts, download and install the packages manually, the process is pretty straightforward.
If you want to push the Arduino binary to a remote machine where the Arduino is connected, you also need to install the Python Fabric package on the host computer. On the remote computer (the GuruPlug in my case), you need to install Python Serial and avrdude. Again if your remote computer is a Linux box like the GuruPlug, you should be able to run
apt-get to install the packages.
Once you have these packages installed, download the SConstruct file and put it in your Arduino project's directory. If you plan on remotely pushing the binary, download the pulsedtr.py file and place it in the same directory.
Building your Arduino project
After you install everything, you should be able to build your project using this command:
This will only build the project. To build it and upload it to an Arduino connected to the same computer, run this:
If you have a single Arduino board connected to your host computer,
scons should be able to identify it automatically. If for some reason it doesn't, run
scons like this:
scons ARDUINO_PORT=/dev/<arduino-tty-device> upload
If your Arduino board is not a Duemilanove, you might need to specify the board type using
ARDUINO_BOARD. To see a list of possible values for this argument, run scons like this:
Beside the parameters defined above, a few others can be defined in the command line:
ARDUINO_HOME used to specify the location of your Arduino IDE package
ARDUINO_VER the version of the Arduino IDE
EXTRA_LIB if you have additional libraries that you'd like to use for the build, specify that directory using this argument.
For additional parameters that you can specify, look in the SConstruct file.
Deploying to an Arduino board connected to a remote computer
The most complex scenario is when you develop on a machine and push the binary to an Arduino connected to a remote computer (in my case that's a GuruPlug). This usually happens after the development phase, when you deploy your Arduino/GuruPlug project. At this point you don't want to take out the Arduino board from inside your project's box. This option allows you to reprogram the Arduino remotely, directly from your development machine.
To do this, you need to make sure you can login to the remote computer (GuruPlug in my case) using ssh without being asked for a password. You can do this by creating a new RSA key and placing the .pub key in the
~/.ssh/authorized_keys on the remote computer. Let's say your new key is in
~/.ssh/guruplug. You should be able to do this from your host computer, and be logged in to the remote machine automatically (obviously you need to replace user and remote-machine accordingly:
ssh -i ~/.ssh/guruplug user@remote-machine
You can now run scons and have it build, push the Arduino binary to the remote system, and upload the binary to the Arduino board connected to the remote system. Here's how:
scons REMOTE_HOST_STRING=user@remote-machine REMOTE_BOARD=atmega328 \
REMOTE_TTY=/dev/ttyUSB0 SSH_KEY=~/.ssh/guruplug1 remote
I ran into a strange while trying to push the binary to the remote system using
fabric. From the stack trace the error was inside
paramiko, the library on which
fabric is based. This problem was happening even after I created a new RSA key without a password. Each time I would run
scons I got this error:
SSHException: invalid key
The error turned out to be inside
~/.ssh/known_hosts which apparently contained an invalid key, or a key
paramiko couldn't parse properly. I removed all the suspect keys from it, and the error disappeared.