Programming Arduino from another Arduino

I started working on a project to to wirelessly program an Arduino from another Arduino. I was thinking of using either a nordic or xbee radio for this. I thought that loading a sketch onto an Arduino was as simple as writing the hex to the serial after it resets, but after some more investigation it seems much more complicated.

This is how I imagined the communication

host reads hex and transfers => wireless tx (nordic or xbee) => rx => arduino (resets target arduino and sends hex) => serial => arduino

I'm wondering if this is even possible? I've seen examples of using xbee to transmit a sketch in transparent mode, using avrdude, but I can't use transparent mode for my application.

Try taking a look at the Fio, it is set up for wireless programming.
http://arduino.cc/en/Main/ArduinoBoardFio

The FIO looks interesting however it wouldn't work for my projects. I use series 2 xbee in api (packet mode). The wireless programming with xbee technique that is used by fio or any arduino/xbee combination relies on transparent mode and triggering the reset with RTS. (with a jumper between the RTS pin and D3).

Transparent mode is fine for 2 xbees but is not viable to managing a network of radios

I've looked at the avrdude upload communication. It starts out with

Connecting to programmer: .avrdude: Send: . [1b]
avrdude: Send: S [53]
avrdude: Recv: C [43]

It gets some info from the Arduino, then later it appears to start to start sending the program (hex), in packets of ~100 bytes

avrdude: reading input file "/var/folders/g1/vflh_srj3gb8zvpx_r5b9phw0000gn/T/build7894685636042281662.tmp/WirelessArduinoProgramming.cpp.hex"
avrdude: writing flash (10750 bytes):

Writing | avrdude: Send: A [41] . [00] . [00]
avrdude: Recv: . [0d]
avrdude: Send: B [42] . [00] . [80] F [46] . [0c] . [94] . [c7] . [01] . [0c] . [94] . [ef] . [01] . [0c] . [94] . [ef] . [01] . [0c] . [94] . [ef] . [01] .

Then I see a verification where it receives the program from the bootloader

Reading | avrdude: Send: A [41] . [00] . [00]
avrdude: Recv: . [0d]
avrdude: Send: g [67] . [00] . [80] F [46]
avrdude: Recv: . [0c] . [94] . [c7] . [01] . [0c] . [94] . [ef] . [01] . [0c] . [94] . [ef] . [01] . [0c] . [94] . [ef] . [01] . [0c] . [94] . [ef] . [01] ...

I was hoping to find this protocol has been implemented in Arduino already. I was looking at the ArduinoISP code

https://raw.githubusercontent.com/adafruit/ArduinoISP/master/ArduinoISP.ino

But that appears to be about programming an AVR, not an Arduino with a bootloader.

I've also looked at the the Electric Imp's Tomatoless boot, which performs arduino programming over wifi

So wondering if the same thing could be done by an Arduino? I'm not sure how tolerant the bootloader is to the timing of the data exchange. I'd be receiving the hex program via packets.

That sonuds cool.

I spent some time this weekend porting Tomatoless to Arduino (device side) and the client side to Java.

The arduino sketch resets the other arduino, then does some basic checks (check_duino) function below. I send:

STK_GET_PARAMETER (0x41), 0x81, CRC_EOP (0x20)

and get back

0x80, 0x98, 0x66

but I expect (according to Tomatoless):

Arduino out: Expected STK_INSYNC but was 0x80
Arduino out: Expected STK_OK but was 0x66

I'm guessing this may be because I'm not running the optiboot bootloader. I'm using an old 2007 Diecimila since that's all I have that is not Leonardo at this time (can't use Leonardo since it requires usb-serial for programming, not available from an Arduino)

I think I'm talking to the bootloader since my sketch doesn't send any serial out.

Current plan is to try loading optiboot and see if that makes any difference

If anyone has ideas on what's going on I'd appreciate it.

int check_duino() {
  clear_read();
    
    int data_len = 0;
    
    // Check everything we can check to ensure we are speaking to the correct boot loader
    cmd_buffer[0] = 0x81;
    data_len = send(STK_GET_PARAMETER, cmd_buffer, 0, 1, 1);
    
    if (data_len == -1) {
     return -1;   
    }
    
    Serial.print("Major is "); Serial.println(read_buffer[0], HEX);
    
    cmd_buffer[0] = 0x82;
    data_len = send(STK_GET_PARAMETER, cmd_buffer, 0, 1, 1);

    if (data_len == -1) {
     return -1;   
    }

    Serial.print("Minor is "); Serial.println(read_buffer[0], HEX);    
    
    cmd_buffer[0] = 0x83;
    data_len = send(STK_GET_PARAMETER, cmd_buffer, 0, 1, 1);
    
    if (data_len == -1) {
      return -1;   
    } else if (read_buffer[0] != 0x3) {
      Serial.print("Expected 0x3 but was "); Serial.println(read_buffer[0]);    
      return -1;
    }
    
    send(STK_READ_SIGN, NULL, 0, 0, 3);
    
    if (data_len != 3) {
      return -1;      
    } else if (read_buffer[0] != 0x1E && read_buffer[1] != 0x95 && read_buffer[2] != 0x0F) {
      Serial.print("Signature invalid");
      return -1;
    }
}

Still stuck on this. I'm sending over a simple STK_GET_PARAMETER for major version:

0x41, 0x81, 0x20

I noticed that changing the baud rates results in different responses:

// 9600 results in 48,65,6C (hex)
// 19200 results in 80,98,66 (hex)
// 115200 results in 0,0,0

The diecimila optiboot is configured at 115200 but I get zeros. I'm certain I'm taking to the bootloader, not the sketch since it I don't reset I get nothing back. Also the sketch doesn't send any serial data.

Sending like so

Serial1.print((char) 0x41);
Serial1.print((char) 0x81);
Serial1.print((char) 0x20);

oops, should have been Serial.write((char) 0x41) instead of print. Of course print sends two bytes, for 4 and 1, instead of the byte 41. Next time I’ll read the documentation!