I got impatient with the arduino IDE because I can't even get it to display line numbers or jump to the line number of an error. Sheesh, what do I even want an IDE for if it can't do that. Pry emacs from my cold dead fingers etc. etc.
Anyway so I was trying to piece together instructions for how to run the good ol'-fashioned commandline compiler and uploader from
the Makefile mentioned here, and after doing a lot of hide-and-seek with fiddly configuration parameters hiding in /usr/share/arduino/hardware/MaKeyMaKey/boards.txt and figuring out where to put them, I had an avrdude commandline that seemed like it should work:
$ /usr/share/arduino/hardware/tools/avrdude -C/usr/share/arduino/hardware/tools/avrdude.conf \
-patmega32u4 -cavr109 -P/dev/ttyACM0 -b57600 -D -Uflash:w:/tmp/makey.hex
Only trouble is, it didn't. When talking over the usb serial port on /dev/ttyACM0, it found itself inadvertently communicating with whatever program was previously running on the board itself. So when the program running spat out a "4" every second or so as debugging information, the uploader would auto-detect some id field of the programmer as "4 4 4 4 4". Clearly wrong! Obviously it should somehow reset the board and prepare it for reprogramming, but... I thought it was avrdude's job to do that. I didn't understand what was failing.
I ran strace* on the arduino ide to find out what exactly it was execve'ing. Turns out, a commandline very much like the above. When I tried running the ide's command manually, it also failed. I was nearly running out of ideas for what the ide could possibly be doing that I wasn't (maybe weird ioctls on the serial port or something?) until I started nosing into the arduino ide source code, and found, in AvrdudeUploader.java, the following tell-tale comment: (the MaKeyMaKey board is indeed Leonardo-derived, I think)
// need to do a little dance for Leonardo and derivatives:
// open then close the port at the magic baudrate (usually 1200 bps) first
// to signal to the sketch that it should reset into bootloader. after doing
// this wait a moment for the bootloader to enumerate. On Windows, also must
// deal with the fact that the COM port number changes from bootloader to
// sketch.
Thus, I tried running
$ screen /dev/ttyACM0 1200 in a shell, and closing the connection immediately, and then running avrdude.
Which, of course, caused it to work.
I am a little shocked that this works as an out-of-band signalling mechanism. How does the board even know that a serial connection was opened and closed at a certain baudrate? I am betraying my ignorance of how serial protocols work at a low level, since there surely must be some negotiation happening that gives it away.
(*I ♥ strace soooo much, it can't be said enough.)