14 August 2013

Arduino, Day 1

Warning for Planet Haskell readers: imperative content! (Or, I tried to get GHC working in 2048 bytes of RAM but didn't get very far...)

A friend of mine sent me a RedBoard and asked me to collaborate with him on a development idea. So I'm playing with an Arduino-compatible device for the first time. I've been aware of the Arduino devices, and the maker... erm, what, "movement?" But just never got one. There were various reasons -- one was that after writing embedded code all day, what I've wanted to do with my time off is not necessarily write more embedded code. Another was that they are very small. But I'm always interested in learning something new, or maybe something quite old -- maybe a board designed specifically for hobbyists might help me revive some of my childhood interest in electronics.

So, I downloaded the Arduino IDE and checked that out a bit. There are some things about the way it's presented that drive me a little batty. The language is C++, but Arduino calls it the "Arduino Programming Language" -- it even has its own language reference page. Down at the bottom the fine print says "The Arduino language is based on C/C++." Ummm. What?

That really makes me mad. First, it seems to give the Arduino team credit for creating something that they really haven't. That team deserves plenty of credit -- not least for building a very useful library -- but they did not, repeat, did not, invent a programming language.

Second, it fails to give credit (and blame) for the language to the large number of people who actually designed and implemented C, C++, and the GCC cross-compiler running behind the scenes, with its reduced standard libraries and all. Of course, it's not that the Arduino team was really trying to hide the fact that there's an open-source compiler inside, but calling it the "Arduino Programming Langage" obfuscates this reality, at the very least.

And third, it obfuscates what programmers are actually learning -- specifically, the distinction between a language and a library.

That might keep things simpler for beginners but this is supposed to be a teaching tool, isn't it? I don't think it's a good idea to obfuscate the difference between the core language (for example, bitwise and arithmetic operators), macros (like min), and functions in the standard Arduino libraries. The Arduino documentation muddles this distinction. But it's important -- for one thing, errors in using each of these will result in profoundly different kinds of diagnostic messages or other failure modes. It also obfuscates something important for experts. That something is "which C++ is this?"

C++ has many variations now. Can I use enum classes or other C++11 features? I don't know, and because of the facade that Arduino is a distinct language, it is harder to find out. They even have the gall to list true and false as constants. Sure, they are constants, but that doesn't mean C and C++ have an actual, useful Boolean type. In fact, they can't, for reasons of backwards compatibility. And if there's one thing C and C++ programmers know, and beginners need to learn quickly, it's that logical truth in C and C++ is messy and requires vigilance. I would hate to have to explain to a beginner why testing a masked bit that is not equal to one against true does not give the expected result.

Anyway, all that aside, this is C++ where the IDE does a few hidden things for you when you compile your code. It inserts a standard header, Arduino.h. It links you to a standard main(). I guess that's all helpful. But finally, it generates prototypes for your functions. That implies a parsing stage, via a separate tool that is not a C++ compiler. If that very thought doesn't cause your brow to furrow, you don't know much about C++.

But let's talk about plugging in the Redboard. On my Mac Pro running Mountain Lion, the board was not recognized as a serial device at all. There's some kind of workaround for this but I just switched over to Ubuntu 12.04 on a ThinkPad laptop. The IDE works flawlessly. I tried to follow some directions to see where the code was actually built by engaging a verbose mode for compilation and uploading, but I couldn't get that working. So I decided that the IDE was obfuscating more than helping, and ditched it.

This was fairly easy, with the caveat that there are a bunch of outdated tools out there, and outdated blog posts explaining how to do it. I went down some dead ends and rabbit holes, but the procedure is really not hard. Ultimately, I used sudo apt-get install to install arduino-core and arduino-mk.

There is now a common Arduino.mk makefile in my /usr/share/arduino directory and I can make project folders with makefiles that refer to it. To make this work I had to add a new export to my .bashrc file, export ARDUINO_DIR=/usr/share/arduino (your mileage may vary depending on how your Linux version works, but that's where I define additional environment variables).

The Makefile in my project directory has the following in it:

BOARD_TAG    = uno
ARDUINO_PORT = /dev/serial/by-id/usb-*
include /usr/share/arduino/Arduino.mk

And that's it. Nothing else. Everything else is inherited from the common Arduino.mk. I can throw .cpp and .h files in there and make builds them and make upload uploads them using the amusingly named utility avrdude (the name stands for AVR Downloader/UploaDEr... er, Dude, that's OK, you can just call it AVRDUDE because you're into AVR chips and you wrote it, that's good enough...

If you have trouble with the upload (wait, is this an "upload" or a "download?" I always thought of "down" as being "downstream" from the bigger computer/data store/ocean to the smaller computer/pond -- like from the Internet to your computer -- but I guess you can call it whatever you want) you might take a look at your devices. A little experimentation (listing the contents of /dev before and after unpluging the board) reveals that the RedBoard is showing up on my system as a device under /dev/serial -- in my case, /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A601EGHT-if00-port0 and /dev/serial/by-path/pci-0000:00:1d.0-usb-0:2:1.0-port0 (your values will no doubt vary). That's why my Makefile reads ARDUINO_PORT = /dev/serial/by-id/usb-* -- so it will catch anything that shows up in there with the usb- prefix. Of course, if your device is showing up elsewhere, or you have more than one device, you might need to tweak this to properly identify your board.

When you look at the basic blink demo program in the Arduino IDE, you see this, the contents of an .ino file (except that I have removed the comments):

int led = 13;

void setup() {                
    pinMode(led, OUTPUT);     
}

void loop() {
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

The Makefile knows how to build an .ino file and inserts the necessary header, implementation of main, and generates any necessary prototypes. Here's what the file looks like to the compiler (and if you want to build this code with make as a .cpp file, it needs to look more like this):

#include <Arduino.h>

int led = 13;

void setup() {
    pinMode(led, OUTPUT);
}

void loop() {
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

int main(void)
{
    init();

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }

return 0;

}

And there it is -- C++, make, and no IDE. Relaxen and watchen Das blinkenlights!

No comments: