Archive for September 2019

Arduino Wireless Bootloading Using Series 2 XBees and XBeeBoot

I built a line-following robot a few months back using an Arduino Uno. I soon found out that connecting the Arduino to the PC with a USB cable every time I wanted to upload a new sketch got old very quickly, especially during debugging and testing. I had some XBee Series 2 RF modules from Digi and thought, “Wouldn’t it be great to do wireless bootloading with the Arduino so I could ditch the USB cable?”. Fortunately, many people around the internet have contributed to making this possible. The following pseudo-walkthrough is based on my notes of how I got it working on Windows, using David Sainty’s XBeeBoot boot loader.

But before we start, a special thank you to David Sainty for answering my questions when I first struggled to get things working.

NOTE: This post isn’t intended to provide every detailed step needed to get things working – you’ll need familiarity with Windows, Linux, C/C++ programming, BASH shell scripting, Arudinos, the Arudino IDE, etc. 

Background

There are a few guides of varying quality floating around the web explaining how to do this. I had a few false starts but I ultimately got David Sainty’s XBeeBoot working on Windows with a stock Arduino Uno. The following is a rundown of the sources I reviewed before I ultimately got it working:

Hardware you’ll need

  • Arduino UNO R3 with 5V ATMega328P running at 16MHz
    •  I first used the pre-built bootloader from the git repo, with a hardcoded 9600 baud rate, then built and tested the atmega328 bootloader at hardcoded rates of 9600, 19200, 38400, 57600, and 115200 baud.
  • At least two XBee Series 2 modules
    • I have S2C XBees but it should work with the S2B and older Series 2 versions. The great thing about the S2C is that it has all the Router/Coordinator/API/AT flavors of the firmware bundled together in flash so you don’t need to change the firmware when you want to change operating modes. It may work with Series 3 as well, I don’t own any Series 3 XBees so I haven’t tried it yet.
  • Something to connect the XBee to your computer
  • Something to connect the XBee to your Arduino.
    • I used a Sparkfun XBee Shield because it was easy but there are many ways to do it. You can wire the XBee up on a breadboard but you’ll need a breakout board or some other means of converting the 2mm pins on the XBee to the .1″ pins on a breadboard.
  • An AVR programmer to write the XBeeBoot bootloader to the Arduino.
    • I had a spare Arduino Uno sitting around which can be used as a programmer. Sparkfun has a tutorial on Installing an Arduino Bootloader using this method that is very easy to follow.

Step 1: Build avrdude for Windows

(If you give a mouse a cookie…)

The avrdude (version 6.3 as of this writing) that comes bundled with the Arduino IDE does not support the protocol needed to upload sketches via XBee. You’ll need to get the source code for avrdude and build it, then patch it to support the XBee protocol. Doing so on Windows turned out to be a bit more involved than I anticipated, but it’s still doable. The easiest way to build avrdude for Windows is to use the build scripts that the Arduino team provides: https://github.com/arduino/avrdude-build-script

Step 1a – Install Windows Subsystem for Linux

As you read the instructions for how the Arduino team builds avrdude for Windows, you soon learn that the standard way is to build it on Linux and cross-compile for Windows. There have been attempts to create native Cygwin and Mingw builds but these have all been abandoned. In a Windows environment, the easiest way to build on Windows without the hassle of dual-booting to Linux is to install the Windows Subsystem for Linux.  There are many online guides available to walk you through this. I followed instructions, chose the Ubuntu option, and viola!, I have an Ubuntu terminal on Windows without needing to dual boot:

Ubuntu on Windows

Ubuntu On Windows

Step 1b – Install the packages needed for building avrdude

Next I had to install the packages used for building avrdude. The Arduino avrdude build script state that you’ll need the gcc-mingw-w64-i686 package but in reality you need several more:

  • devtools-essential (includes make and gcc)
  • build-essential
  • bison
  • zip
  • automake
  • flex
  • pkg-config
  • g++-mingw-w64-i686
  • gcc-mingw-w64-i686
  • libtool
Steb 1c – Clone the Arduino avrdude build script repo and follow the instructions for cross-compiling on windows.

I ran into one hiccup I will note here, but your mileage will probably vary depending on what versions of tools you have:

  • The configure script for libelf has a check for the size of 64- and 32-bit integers that kept failing. On 64-bit Windows, ints and longs are typically 32-bits while a 64-bit integer is a long long. The test was failing trying to compare a string value of “4” (bytes) to the numeric value of 4. If I had more bash shell kung-fu I could have probably fixed this more elegantly but I simply commented out the check and manually defined int as int32, long as int32, and long long as int64.

If the build is successful you’ll end up with a .zip file containing a Windows executable for avrdude and the avrdude.conf file. You should extract the zip package to a location preferably without spaces in the pathname (your command-line life is just easier when there aren’t spaces in the path) and add the location to your Windows PATH environment variable (in Powershell its $Env:Path):


PS C:\Users\Brian> which avrdude 
C:\Users\Brian\avrdude\bin\avrdude.exe

It’s probably a good idea to verify that your custom-built version of avrdude actually works. I uploaded a sketch with it just to make sure.

NOTE 1: You can choose to overwrite the Arduino IDE-supplied version of avrdude if you wish, but I kept the two versions separate.

NOTE 2: You may want to modify the build scripts to give your version of avrdude a custom version. I forgot to do this.

NOTE 3: The build script actually downloads the avrdude source and patches it with Arduino-specific patches so you have to build it before you can patch it with the XBee protocol as the avrdude source isn’t checked into the Arduino repo.

NOTE 4: In Powershell I have a function in my startup script to emulate the which command.

Step 1d – Patch avrdude

Now you need to actually add the XBee protocol to avrdude. David Sainty created a patch for avrdude back in 2015 but it never got included into the avrdude core. Unfortunately, avrdude has changed a bit so the patch doesn’t work out of the box and requires some tweaking.  I patchd the avrdude source and re-built it using the following steps:

  • Patched pgm_type.c, makefile.am, avrdude.conf.in, and xbee.h according to the patch file – these work without changes.
  • For xbee.c, there there is no longer any pgm.h or serial.h in the latest versions of avrdude. They were replaced by libavrdude.h so you’ll need to modify xbee.c to remove the references to those two include files and add the reference to libavrdude.h.
  • Comment out the line in package-avrdude.bash that removes all already-built directories, should be around line 88. Don’t want to wipe out all the build work we’ve already done and our newly patched code. 
  • Comment out the line in avrdude-6.3.build.bash that patches avrdude with the Arduino-specific patches. It was already patched in the first build.
  • Comment out the unnecessary git clone in avrdude-6.3.build.bash as we already have cloned avrdude in the first go round.
  • Build as previously.

If all goes well you will have a version of avrdude that supports the Xbee protocol. I extracted the contents of my .zip file like before and made sure the XBee patched version worked with a regular sketch before I tried to do anything wirelessly with the XBee bootloader.

Step 2 – Burn the XBee bootloader

There are many guides on the internet for how to burn a bootloader. I happened to have a spare Arduino that I used as an ISP.  In theory you can put the boards-1.6.txt files and the pre-built bootloader in the right location to burn the bootloader from the Arduino IDE but I did it on the command line following the Sparkfun tutorial. The trick is getting the fuse bits right.

Erase the chip and set the fuse bits and lock bits:


PS C:\Users\Brian> avrdude -v -v -CC:\Users\Brian\avrdude\etc\avrdude.conf -P COM3 -b 19200 -c arduino -p atmega328p -e -Ulock:w:0x3F:m -Ulfuse:w:0xFF:m -Uhfuse:w:0xDC:m -Uefuse:w:0xFD:m

avrdude.exe: Version 6.3-20190619
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is "C:\Users\Brian\avrdude\etc\avrdude.conf"

Using Port : COM3
Using Programmer : arduino
Overriding Baud Rate : 19200
AVR Part : ATmega328P
Chip Erase delay : 9000 us
PAGEL : PD7
BS2 : PC2
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :

Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00

Programmer Type : Arduino
Description : Arduino
Hardware Version: 2
Firmware Version: 1.18
Topcard : Unknown
Vtarget : 0.0 V
Varef : 0.0 V
Oscillator : Off
SCK period : 0.1 us

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: safemode: lfuse reads as FF
avrdude.exe: safemode: hfuse reads as DC
avrdude.exe: safemode: efuse reads as FD
avrdude.exe: erasing chip
avrdude.exe: reading input file "0x3F"
avrdude.exe: writing lock (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude.exe: 1 bytes of lock written
avrdude.exe: verifying lock memory against 0x3F:
avrdude.exe: load data lock data from input file 0x3F:
avrdude.exe: input file 0x3F contains 1 bytes
avrdude.exe: reading on-chip lock data:

Reading | ################################################## | 100% 0.02s

avrdude.exe: verifying ...
avrdude.exe: 1 bytes of lock verified
avrdude.exe: reading input file "0xFF"
avrdude.exe: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.00s

avrdude.exe: 1 bytes of lfuse written
avrdude.exe: verifying lfuse memory against 0xFF:
avrdude.exe: load data lfuse data from input file 0xFF:
avrdude.exe: input file 0xFF contains 1 bytes
avrdude.exe: reading on-chip lfuse data:

Reading | ################################################## | 100% -0.00s

avrdude.exe: verifying ...
avrdude.exe: 1 bytes of lfuse verified
avrdude.exe: reading input file "0xDC"
avrdude.exe: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude.exe: 1 bytes of hfuse written
avrdude.exe: verifying hfuse memory against 0xDC:
avrdude.exe: load data hfuse data from input file 0xDC:
avrdude.exe: input file 0xDC contains 1 bytes
avrdude.exe: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.02s

avrdude.exe: verifying ...
avrdude.exe: 1 bytes of hfuse verified
avrdude.exe: reading input file "0xFD"
avrdude.exe: writing efuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude.exe: 1 bytes of efuse written
avrdude.exe: verifying efuse memory against 0xFD:
avrdude.exe: load data efuse data from input file 0xFD:
avrdude.exe: input file 0xFD contains 1 bytes
avrdude.exe: reading on-chip efuse data:

Reading | ################################################## | 100% 0.02s

avrdude.exe: verifying ...
avrdude.exe: 1 bytes of efuse verified

avrdude.exe: safemode: lfuse reads as FF
avrdude.exe: safemode: hfuse reads as DC
avrdude.exe: safemode: efuse reads as FD
avrdude.exe: safemode: Fuses OK (E:FD, H:DC, L:FF)

avrdude.exe done. Thank you.

NOTE1: when using an arduino as an ISP the Arduino as ISP sketch is hardcoded to 19200 baud. 

NOTE2: The XBeeboot loader fits in 1KB so I used Engbedded’s fuse calculator to figure out the right fuse bits for a 1024 byte (512 words) bootloader size .

Next, upload the XBeeBoot bootloader and set the lock bits:


PS C:\Users\Brian> avrdude -v -v -CC:\Users\Brian\avrdude\etc\avrdude.conf -P COM3 -b 19200 -c arduino -p atmega328p -Uflash:w:C:\Users\Brian\Projects\xbeeboot\xbeeboot\bootloaders\xbeeboot\xbeeboot_atmega328.hex:i -Ulock:w:0x0F:m
avrdude.exe: Version 6.3-20190619
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is "C:\Users\Brian\avrdude\etc\avrdude.conf"

Using Port : COM3
Using Programmer : arduino
Overriding Baud Rate : 19200
AVR Part : ATmega328P
Chip Erase delay : 9000 us
PAGEL : PD7
BS2 : PC2
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :

Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00

Programmer Type : Arduino
Description : Arduino
Hardware Version: 2
Firmware Version: 1.18
Topcard : Unknown
Vtarget : 0.0 V
Varef : 0.0 V
Oscillator : Off
SCK period : 0.1 us

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: safemode: lfuse reads as FF
avrdude.exe: safemode: hfuse reads as DC
avrdude.exe: safemode: efuse reads as FD
avrdude.exe: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude.exe: erasing chip
avrdude.exe: reading input file "C:\Users\Brian\Projects\xbeeboot\xbeeboot\bootloaders\xbeeboot\xbeeboot_atmega328.hex"
avrdude.exe: writing flash (32768 bytes):

Writing | ################################################## | 100% 0.03s

avrdude.exe: 32768 bytes of flash written
avrdude.exe: verifying flash memory against C:\Users\Brian\Projects\xbeeboot\xbeeboot\bootloaders\xbeeboot\xbeeboot_atmega328.hex:
avrdude.exe: load data flash data from input file C:\Users\Brian\Projects\xbeeboot\xbeeboot\bootloaders\xbeeboot\xbeeboot_atmega328.hex:
avrdude.exe: input file C:\Users\Brian\Projects\xbeeboot\xbeeboot\bootloaders\xbeeboot\xbeeboot_atmega328.hex contains 32768 bytes
avrdude.exe: reading on-chip flash data:

Reading | ################################################## | 100% 0.03s

avrdude.exe: verifying ...
avrdude.exe: 32768 bytes of flash verified
avrdude.exe: reading input file "0x0F"
avrdude.exe: writing lock (1 bytes):

Writing | ################################################## | 100% 0.04s

avrdude.exe: 1 bytes of lock written
avrdude.exe: verifying lock memory against 0x0F:
avrdude.exe: load data lock data from input file 0x0F:
avrdude.exe: input file 0x0F contains 1 bytes
avrdude.exe: reading on-chip lock data:

Reading | ################################################## | 100% 0.02s

avrdude.exe: verifying ...
avrdude.exe: 1 bytes of lock verified

avrdude.exe: safemode: lfuse reads as FF
avrdude.exe: safemode: hfuse reads as DC
avrdude.exe: safemode: efuse reads as FD
avrdude.exe: safemode: Fuses OK (E:FD, H:DC, L:FF)

avrdude.exe done. Thank you.

NOTE3: The atmega328 bootloader inthe XBeeBoot repo is hard coded to 9600 baud; however, because a standard Arduino Uno has an external crystal and UART, it should be able to support higher baud rates. I recompiled the bootloader with a hardcoded 19200 buad rate and it worked great. I haven’t tried higher baud rates yet.

Step 3 – Configure your XBees and hardware

I used XCTU, which only runs on Windows, to configure the XBee coordinate and router radios. Sparkfun’s original wireless bootloading tutorial provides a guide for configuring your XBees ; however, it’s a bit outdated and is intended for Series 1 XBees. You’ll need your Series 2 XBees configured for API mode.

Here is the config for the base, acting as coordinator:

Networking

Set the PAN ID and enable as a coordinator

Addressing

Destination High and Low of the router

Serial Interfacing

API mode enabled with escaping

I/O Settings

D3 set to input

I/O Sampling

Monitor D3 for changes

NOTE: I didn’t have encryption or sleep modes or other advanced features enabled but this should work seamlessly with those features.

Here is the config for the remote, as router:

Networking

Same PAN ID

Addressing

High and low address of the Coordinator

Serial Interfacing

Router_serial

Also API mode with escaping

 I/O

Pin3 is output (from the xbee to the Arudino)

You should also wire your hardware up as described in the XBeeBoot github page or in the Sparkfun tutorial. In my case, the Sparkfun XBee shield took care of connecting the XBee DOUT (pin 2) to the Atmega RXD (Atmega328P pin 2/ Arduino Uno 0), and connecting the XBee DIN (pin 3) to the Atmega TXD (Atmega328P pin 3, Arduino Uno 1). All I had to do was connect the XBee DIO3 pin (pin 17) to the Atmega RESET pin (Atmega328P pin 1/ Arduino Uno RESET). The XBee shield made this pretty easy with the through-hole breakouts as shown in the following picture (I skipped the capacitor):

XBeeShield

XBeeShield with DIO3 to RESET

NOTE: If you are using an XBee shield, you’ll want to set the DLINE/UART switch to UART because the bootloader doesn’t have any way of knowing that you’re sending and receiving on Arduino Uno digital pins 2 and 3 if you have the switch set to DLINE. As the Sparkfun hookup guide cautions, remember to set the switch back to DLINE if you’re going to upload a sketch via USB cable with rather than wirelessly via the XBee.

Step 4 – Enjoy Wirelessly Bootloading!

Create a sketch using the Adduino IDE; however, instead of uploading the sketch to an Arduino from the IDE, select Export Compiled Binary from the menu. This will create the .hex file in the directory where your sketch is saved:


PS C:\Users\Brian\Projects\Arduino\Blinktest> dir
Directory: C:\Users\Brian\Projects\Arduino\Blinktest
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 9/15/2019 8:45 PM 1838 Blinktest.ino
-a---- 9/15/2019 8:45 PM 2889 Blinktest.ino.standard.hex
-a---- 9/15/2019 8:45 PM 4244 Blinktest.ino.with_bootloader.standard.hex

You can then use your version of avrdude with the XBee protocol to upload:

PS C:\Users\Brian\Projects\Arduino\Blinktest> avrdude -CC:\Users\Brian\avrdude\etc\avrdude.conf -b 19200 -c xbee -p atmega328p -u -Uflash:w:BlinkTest.ino.standard.hex:i -P0013A20040DDE8FD@COM5

avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.08s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude.exe: erasing chip
avrdude.exe: reading input file "BlinkTest.ino.standard.hex"
avrdude.exe: writing flash (1022 bytes):

Writing | ################################################## | 100% 3.76s

avrdude.exe: 1022 bytes of flash written
avrdude.exe: verifying flash memory against BlinkTest.ino.standard.hex:
avrdude.exe: load data flash data from input file BlinkTest.ino.standard.hex:
avrdude.exe: input file BlinkTest.ino.standard.hex contains 1022 bytes
avrdude.exe: reading on-chip flash data:

Reading | ################################################## | 100% 3.18s

avrdude.exe: verifying ...
avrdude.exe: 1022 bytes of flash verified

avrdude.exe done. Thank you.

NOTE1: In this example I was using a version of the bootloader compiled for 19200 baud. You’ll want to change the baud rate to 9600 (and your XBees should be configured for that as well) if you’re using the bootloader directly from the XBeeBoot repo.

A Note on Higher Baud Rates

You can build the XBeeBoot bootloader to use higher baud rates; however, your mileage may vary given your configuration. I compiled a version of the bootloader at 9600, 19200, 38400, 57600, and 115200 baud and tested all of them successfully on the Arudino Uno. I didn’t build or test the bootlaoder for any other devices such as the Arudino Pro because I don’t have any other devices. Remember that you also need to configure your XBees with the appropriate baud rate.

For example, the following shows the successful wireless upload of a 20kb sketch at 115200 baud:


avrdude -CC:\Users\Brian\avrdude\etc\avrdude.conf -b 115200 -c xbee -p atmega328p -u -Uflash:w:20kbtest.ino.standard.hex:i -P0013A20040DDE8FD@COM5 
avrdude.exe: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.05s

avrdude.exe: Device signature = 0x1e950f (probably m328p)
avrdude.exe: NOTE: "flash" memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude.exe: erasing chip
avrdude.exe: reading input file "20kbtest.ino.standard.hex"
avrdude.exe: writing flash (20006 bytes):

Writing | ################################################## | 100% 39.36s

avrdude.exe: 20006 bytes of flash written
avrdude.exe: verifying flash memory against 20kbtest.ino.standard.hex:
avrdude.exe: load data flash data from input file 20kbtest.ino.standard.hex:
avrdude.exe: input file 20kbtest.ino.standard.hex contains 20006 bytes
avrdude.exe: reading on-chip flash data:

Reading | ################################################## | 100% 34.32s

avrdude.exe: verifying ...
avrdude.exe: 20006 bytes of flash verified

avrdude.exe done. Thank you.

The following table shows the actual performance times I noted to write and read 20006 bytes at the different baud rates

Bytes Baud Make Error Write (seconds) Read (seconds)
20006 9600 0.10% 133.46 133.85
20006 19200 0.10% 74.45 62.94
20006 38400 0.10% 47.3 49.89
20006 57600 0.70% 38.7 34.36
20006 115200 2.10% 39.36 34.32

The Make Error is the error reported from a baud rate calculation done at build time. If baud rate check produces more than 2% error you’ll get a warning while building the bootloader but it will still succeed:


brian@MrBurns$ make atmega328
avr-gcc (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

BAUD RATE CHECK: Desired: 115200, Real: 117647, UBRRL = 16, Error=2.1%
avr-gcc -g -Wall -Os -fno-split-wide-types -mrelax -mmcu=atmega328p -DF_CPU=16000000L -DBAUD_RATE=115200 -c -o xbeeboot.o xbeeboot.c
xbeeboot.c:305:6: warning: #warning BAUD_RATE error greater than 2% [-Wcpp]
#warning BAUD_RATE error greater than 2%
^
avr-gcc -g -Wall -Os -fno-split-wide-types -mrelax -mmcu=atmega328p -DF_CPU=16000000L -DBAUD_RATE=115200 -Wl,--section-start=.text=0x7c00 -Wl,--section-start=.version=0x7ffe -Wl,--relax -nostartfiles -nostdlib -o xbeeboot_atmega328.elf xbeeboot.o -lc
avr-size xbeeboot_atmega328.elf
text data bss dec hex filename
978 0 0 978 3d2 xbeeboot_atmega328.elf
avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex xbeeboot_atmega328.elf xbeeboot_atmega328.hex
avr-objdump -h -S xbeeboot_atmega328.elf > xbeeboot_atmega328.lst
rm xbeeboot_atmega328.elf xbeeboot.o

I hope you enjoy wireless bootloading with XBees. Thanks again to David Sainty for the XBeeBoot bootloader and everyone else that has contributed to the ability to wirelessly bootload with XBees.