OTA capable bootloader for arduino

Earlier i’ve found the optiboot project, which looked like a great candidate to reshape into the key instrument to enable remote firmware upgrades for the pro minis.

The idea is to have the flash split logicaally in two:

  • first section running the current firmware
  • second section is reserved for the new firmware

The bootloader is the only “trusted” part of the flash from within flash write commands are accepted.

So: i will add an upgrade service to the bootloader to provide the flash programming feature to the main application.

Creating the bootloader itself

I’ve found earlier a great project (https://github.com/majekw/optiboot ); which enabled spm calls from the application - it looked close enough; but sadly not enough for me ;) But the method he used to create the service is brilliant. Because I wanted to refactor the code another way around I’ve started from the original optiboot - and added my extra features…most importantly the firmware swap feature.

Everything went almost entirely well…until I’ve added the same trick as majekw in it’s own version…the bootloader entrypoint+2 jumps to the service routine directly - (this eliminates the need to create a symbol file to link against; or ad-hoc elf reading & header generation methods).

I’ve kept getting a strange exception every time I tried to compile it:

optiboot.c:865:(.init8+0x2): relocation truncated to fit: R_AVR_13_PCREL against undefined symbol `writebuffer'
collect2: error: ld returned 1 exit status

It turned out that even thru I’ve declared my writeBuffer function as __attribute__((noinline)) it have been inlined anyway by gcc…and when the unlucky assembler came for the function address to hook it up with the reserved service callsite; it got into trouble because the symbol was already thrashed!

I’ve read in some gcc manual that noinline sometimes doesn’t prevent other optimizations…and it recommended that beyond the noinline attribute; I should add some opague stuff to the function like:


I’ve tried it and didn’t worked…but now I know what’s the root of the issue…when I’ve added a dummy call to the function (beyond the existing 1)…the asm block found its symbol!

So I’ve search very hard how to reference my function for the asm part…in a way that gcc too sees that the address of that function should be sacred.

I ended up with this in the end:

  asm volatile (
    "	rjmp	1f\n"
    "	rjmp	writebuffer\n"
    "1:\n" : :
	[F] "i" (&writebuffer)

Assembly experts may notice that the argument of writebuffer is not used…(because that didn’t seemed to work ;)…but asm finds the symbol and gcc is tricked that the argument F is being used ;) So well..not the nicest, but at least it works!

Till this point the “good” state of the bootloader was ending up with some memory space violation errors like:

/usr/lib/gcc/avr/4.8.1/../../../avr/bin/ld: section .version loaded at [0000000000007ffe,0000000000007fff] overlaps section .text loaded at [0000000000007e00,0000000000007fff]
collect2: error: ld returned 1 exit status

Because of this I’ve disabled all non-eseential features:

  • bootloader startup flash - totally irrelevant for remotely updated devices.
  • version number - i’ve kept it, but i’ve dropped the “last 4 bytes of the bootloader is its version number”

I ended the first evening with this result.

Yuhuu!! 512 bytes exactly!

When I’ve first loaded it…I thinked i’ve done something wrong..and it just doesn’t work..it taken a few minutes and reflash cycles for me to realize: those leds are not blinking when you load it…because you have just killed that feature! :)

After loading a basic blink sketch without issues (using eclipseArduino; great tool with refactoring support by the way)…and seeing the board flashing like a charm…I was happy ;)

Now that i’ve a probably functioning bootloader (which only needs heavy refactoring) - I went forward to create the userland part…

Userland prototype&test

I wondered where to put this thing…but since I’ve already cleaned up my optiboot repo from compiled binaries, and in the mean time I’m dependent on killing existing features…I don’t think it will got accepted into the original..so I might add the userland lib to the bootloader repo…it wont hurt; and I’m already using submodules to attach rweather’s crypto…then I might just have to link this one too ;)

Well…the bootloader contained at least one error…the FLASHEND is the last byte; and I needed the size of the FLASH so a +1 was missing…and even the math wasn’t right…but now it works…the biggest obstacle was that I embedded a rookie error in the testprogram:


And it went sideways…it killed the running firmware after a while; and I was left without clues…i’ve written traceable patterns into flash…to get a grip on it…after i’ve found my mistake I realized that: I’m a bit tired for today and went sleeping.

idea: Can i create an stk500 like service?

If I could modify the bootloader to give some kind of stk500 service to the userland; that would be great as i would like to still keep using standard arduino tools to upload firmwares…but instead using wires, thru the ota service.

This turned out to be an idiotic one…after a few wasted hours…i returned to my original plan…after i realized that I will additionaly also need:

  • some kind of multiplexing service to forward stk500-alike commands to the real device…using:
    • either a directly connected rf24 - which is not an option; because this is a laptop - I may use one of my pi’s; but either way thats one hop away…
    • or use the “directly connected” desk arduino (mega2560)…i opted for this; same architecture
  • because i either way have to create some bridge elements…I’m better off implementing those…stk500 can wait; and it doesnt really have contain real value…
  • i will also need a more sophisticated network layer; which can handle packet reassembly - because the nrf24 can only transmitt <128 byte frames…so to be able to flash a whole page - i definetly need that.

enough said…these must be addressed first!

comments powered by Disqus