08/27/2014

How to reboot an Arduino Leonardo / Micro into the bootloader.

One of the things we're building for the Keyboardio Model 01 is an interactive keyboard shell that I've provisionally named "quiche".

Today, I implemented functionality to allow the user to reboot the ATmega32U4 inside the keyboard into the Caterina bootloader so the user can upload new firmware.

It took a lot more time than it should have to find the right magic incantations to get the bootloader to stay in firmware upload mode. (In the end, it was just a matter of looking at CDC.c inside the Arduino core.)

So that nobody else has to stumble through this as I have, I've pasted my solution below.

#include <avr/wdt.h>// [...]// Set the magic bits to get a Caterina-based device
// to reboot into the bootloader and stay there, rather
// than run move onward
//
// These values are the same as those defined in
// Caterina.c
uint16_t bootKey = 0x7777;
uint16_t *const bootKeyPtr = (uint16_t *)0x0800;
// Stash the magic key
*bootKeyPtr = bootKey;
// Set a watchdog timer
wdt_enable(WDTO_120MS);
while(1) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us

Solutions that didn't work for for me included:

Writing raw assembler like:

asm volatile ("jmp 0x7800"); // This address also happens to be bootloader-size dependent

Tweaking MCUSR to try to convince the bootloader that an external reset had been initiated.

Comments

One of the things we're building for the Keyboardio Model 01 is an interactive keyboard shell that I've provisionally named "quiche".

Today, I implemented functionality to allow the user to reboot the ATmega32U4 inside the keyboard into the Caterina bootloader so the user can upload new firmware.

It took a lot more time than it should have to find the right magic incantations to get the bootloader to stay in firmware upload mode. (In the end, it was just a matter of looking at CDC.c inside the Arduino core.)

So that nobody else has to stumble through this as I have, I've pasted my solution below.

#include <avr/wdt.h>// [...]// Set the magic bits to get a Caterina-based device
// to reboot into the bootloader and stay there, rather
// than run move onward
//
// These values are the same as those defined in
// Caterina.c
uint16_t bootKey = 0x7777;
uint16_t *const bootKeyPtr = (uint16_t *)0x0800;
// Stash the magic key
*bootKeyPtr = bootKey;
// Set a watchdog timer
wdt_enable(WDTO_120MS);
while(1) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us

Solutions that didn't work for for me included:

Writing raw assembler like:

asm volatile ("jmp 0x7800"); // This address also happens to be bootloader-size dependent

Tweaking MCUSR to try to convince the bootloader that an external reset had been initiated.