of the two existing methods of uploading code to an arduino: the Arduino IDE from source, and avrdude using a pre-compiled .HEX file, alas i've found neither are achievable by the typical user.
while the Arduino IDE is quite an accomplishment in providing accessibility to microcontroller programming for many users, it is an enormous download and beyond the ability of the average user to master for the single task of updating firmware.
and avrdude is itself still quite large (1mb), consists of four parts (avrdude.exe, avrdude.conf, libusb0.dll, the firmware .HEX to be uploaded), and requires a 'cryptic' command line incantation to make work. admittedly, there are wrappers (from batch files to small GUI applications), but most that i've looked at still leave open the opportunity for the end user to get horribly lost or make mistakes.
my solution consists of a single windows executable (or linux binary) that contains the .HEX file embedded within, which the user just needs to run. port selection can be done via a GUI or specified on the command line.
i started out with a trace output from avrdude:
avrdude -patmega328p -carduino -PCOM5 -b57600 -D -Uflash:w:ICSP_v1C.hex -v -v -v -v >trace.txt 2>&1
the 2>&1 at the end is important under windows, as avrdude seems to direct all of its output to stderr, which normally can not be redirected away from the console. this is perhaps a bit of a bug.
that output provided me with details of how avrdude talks to an arduino. i then got hold of microchip's "AVR061: STK500 Communication Protocol" (doc2525.pdf) and used that to derive meaning from avrdude's output. finally i looked over this page:
http://baldwisdom.com/bootloading/
which provided some hints about what things i didn't need to bother with when talking to an arduino bootloader.
i've now got working code, but i am still not 100% sure it will work with any arduino bootloader. fyi, below is my current code (written in C). the firmware has already been loaded into an array of unsigned char called ICSP (in ICSP_v1C.inc). outside of view is also a serial port unit and assignment of 3 to baud_rate.
i'd welcome comment!
cheers,
rob
if (baud_rate < 5) // invoked by a baud rate of 1, 2, 3, or 4.
{
#include "ICSP_v1C.inc"
int i, n;
unsigned char buffer [140]; // 0x80 + 12d
int bps[] = {0, 9600, 19200, 57600, 115200 }; // known arduino bootloader baud rates
baud_rate = bps[baud_rate];
if (serial_open (port, baud_rate, 100) < 0) {
printf ("failed to configure port\n");
serial_close();
exit (-1);
}
printf("%i baud ", baud_rate);
for (i = 0; i < 40; i++) {
// sprintf ((char*) buffer, "%c%c", 0x30, 0x20);
buffer[0] = 0x30; // get synchronization
buffer[1] = 0x20;
serial_write (buffer, 2);
printf (".");
n = serial_read (buffer, 2);
if ((n == 2) && (buffer[0] == 0x14) && (buffer[1] == 0x10)) i=100;
}
if (i < 100) {
printf (" arduino/STK500 not found\n");
serial_close();
exit (-1);
}
printf (" synchronized\n");
// sprintf ((char*) buffer, "%c%c", 0x50, 0x20);
buffer[0] = 0x50; // enter program mode
buffer[1] = 0x20;
serial_write (buffer, 2);
serial_read (buffer, 2);
if ((n != 2) || (buffer[0] != 0x14) || (buffer[1] != 0x10)) {
printf ("failed to enter program mode\n");
serial_close();
exit (-1);
}
// sprintf ((char*) buffer, "%c%c", 0x75, 0x20);
buffer[0] = 0x75; // read signature bytes (3)
buffer[1] = 0x20;
serial_write (buffer, 2);
n = serial_read (buffer, 5);
if ((n != 5) || (buffer[0] != 0x14) || (buffer[4] != 0x10)) {
printf ("failed to get signature\n");
serial_close();
exit (-1);
}
unsigned ID = (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
printf ("signature = %06x device = %s\n", ID, ID == 0x1e950f ? "ATmega328P" : "(wrong uP)");
for (i = 0; i < sizeof(ICSP) / 0x80; i++) printf (".");
for (i = 0; i < sizeof(ICSP) / 0x80; i++) printf ("\b");
for (i = 0; i < sizeof(ICSP); i += 0x80) {
printf ("#");
// sprintf ((char*) buffer, "%c%c%c%c", 0x55, (i >> 1) % 0x100, (i >> 1) / 0x100, 0x20);
buffer[0] = 0x55; // load address
buffer[1] = (i >> 1) % 0x100; // address low (word boundary)
buffer[2] = (i >> 1) / 0x100; // address high
buffer[3] = 0x20;
serial_write (buffer, 4);
n = serial_read (buffer, 2);
if ((n != 2) || (buffer[0] != 0x14) || (buffer[1] != 0x10)) {
printf ("\nfailed to load address %04x\n", i);
serial_close();
exit (-1);
}
// sprintf ((char*) buffer, "%c%c%c%c", 0x64, 0x00, 0x80, 0x46);
buffer[0] = 0x64; // program page
buffer[1] = 0x00; // length high (in bytes, NOT words)
buffer[2] = 0x80; // length low (order reverse to address)
buffer[3] = 0x46;
memcpy (&buffer[4], &ICSP[i], 0x80); // data (128 bytes)
buffer[4 + 0x80] = 0x20;
serial_write (buffer, 4 + 0x80 + 1);
n = serial_read (buffer, 2);
if ((n != 2) || (buffer[0] != 0x14) || (buffer[1] != 0x10)) {
printf ("\nfailed to program page\n");
serial_close();
exit (-1);
}
}
printf ("\n");
// sprintf ((char*) buffer, "%c%c", 0x51, 0x20);
buffer[0] = 0x51; // leave program mode
buffer[1] = 0x20;
serial_write (buffer, 2);
n = serial_read (buffer, 2);
if ((n != 2) || (buffer[0] != 0x14) || (buffer[1] != 0x10)) {
printf ("failed to exit program mode\n");
serial_close();
exit (-1);
}
printf ("firmware uploaded to 'ascii ICSP' adapter OK\n");
serial_close();
exit (0);
}