My Notebook

How to make the Watchdog timer work on an Arduino Pro Mini by replacing the bootloader

Author
Date
Category
Electronics

I have just spent several hours debugging a very annoying issue with the Arduino Pro Minis Watchdog timer, until I finally realized that the fault wasn't in my code, but in the bootloader. The Watchdog timer is used to reset the microcontroller, if it is non-reactive for a certain period of time. The microcontroller can crash or enter an infinite loop for any number of reasons, so an automatic recovery from these states is an important feature.

The Issue

The following simple sketch illustrates the problem. The statement while(1); creates an infinite loop to simulate a crash. The call to wdt_enable(WDTO_8S); sets the Watchdog timer to 8 seconds.

#include <avr/wdt.h>

#define LED_PIN 13

void setup() {
  // Blink LED to signal reset
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  delay(1000);
  digitalWrite(LED_PIN, LOW);
}

void loop() {
  wdt_enable(WDTO_8S);

  // Infinite loop to simulate a crash
  while (1);
}

So it should blink for a second, go into the infinite loop for 8 seconds, until the Watchdog automatically resets the sketch, and blink again for one second. Instead it blinks for one second, waits for 8 seconds and then sends the bootloader into an infinite reset loop. If the bootloader is in this infinite loop, nothing works anymore. You cannot upload a new sketch and the reset button doesn't work. You have to physically disconnect the power to recover the Arduino Pro Mini.

The sketch works as intended on the Arduino Uno which uses exactly the same chip.

The Source of the Problem

After a reset that was initiated by the Watchdog timer, the Watchdog is still active and the timeout period is automatically set to a very short time. So the very first thing the bootloader should do is deactivate the Watchdog. Apparently this doesn't happen on the Arduino Pro Mini and the Watchdog times out before the bootloader finishes, which leads to another reset.

The Arduino is stuck in its bootloader and the function void setup() is never executed.

Replacing the Arduino Pro Mini Bootloader

It turns out, that the Arduino Uno, which doesn't have the issue, by default comes with optiboot as its bootloader, but the Arduino Pro Mini, which uses exactly the same chip, does not. The obvious solution is to flash optiboot on the Arduino Pro Mini, which is exactly what I did.

Patching boards.txt

At the time of writing it is not possible to add optiboot using the Board manager of the Arduino IDE, but it seems that there is work on the way to make this possible in the near future. In the meantime you can just patch the file boards.txt to point to the optiboot bootloader. It is already used for the Arduino Uno boards, so we just have to copy the settings from there.

On Arch Linux the file boards.txt is located in the following directory: /usr/share/arduino/hardware/arduino/avr/boards.txt

Only the path to the bootloader file and the upload speed have to be changed:

diff --git a/boards.txt b/boards.txt
index 75d93e9..996703c 100644
--- a/boards.txt
+++ b/boards.txt
@@ -635,12 +635,12 @@
 pro.menu.cpu.16MHzatmega328=ATmega328 (5V, 16 MHz)
 
 pro.menu.cpu.16MHzatmega328.upload.maximum_size=30720
 pro.menu.cpu.16MHzatmega328.upload.maximum_data_size=2048
-pro.menu.cpu.16MHzatmega328.upload.speed=57600
+pro.menu.cpu.16MHzatmega328.upload.speed=115200
 
 pro.menu.cpu.16MHzatmega328.bootloader.low_fuses=0xFF
 pro.menu.cpu.16MHzatmega328.bootloader.high_fuses=0xDA
 pro.menu.cpu.16MHzatmega328.bootloader.extended_fuses=0x05
-pro.menu.cpu.16MHzatmega328.bootloader.file=atmega/ATmegaBOOT_168_atmega328.hex
+pro.menu.cpu.16MHzatmega328.bootloader.file=optiboot/optiboot_atmega328.hex
 
 pro.menu.cpu.16MHzatmega328.build.mcu=atmega328p
 pro.menu.cpu.16MHzatmega328.build.f_cpu=16000000L

The above patch can be applied with the following commands. You need to restart the Arduino IDE for the changes to take effect.

cd /usr/share/arduino/hardware/arduino/avr/
sudo git apply ~/patch.diff

Flashing optiboot using another Arduino as ISP

To flash a new bootloader an ISP or In-System-Programmer is needed. You can buy a cheap one online or use another Arduino.

Using another Arduino as ISP

There are lots of tutorials online on how to connect an Arduino Uno as ISP. It is actually quite easy:

  1. Disconnect everything from the Arduino Uno. Go to File->Examples->ArduinoISP and flash that sketch to the Arduino. Arduino IDE menue
  2. Connect the Arduino Uno to the Arduino Pro Mini as shown below and don't forget to add the 10 µF to 100 µF capacitor between the reset pin and ground on the Uno. Updateing bootloader using Arduino ISP
  3. Select the correct board in the Arduino IDE under Tools->Board. Then select "Arduino as ISP" under Tools->Programmer and finally burn the bootloader with Tools->Burn bootloader. The result should look something like this: Burn bootloader

Workaround for similar issues with the Attiny84

The Attiny84 has exactly the same Watchdog issue, but there is a simple workaround. This workaround does not work with the Arduino Pro Mini, so it is still necessary to replace the bootloader.

#include <avr/wdt.h>

#define LED_PIN 0

void setup() {
  /*
   * Has to be the first instruction to prevent
   * an infinite reset loop
   */
  wdt_enable(WDTO_8S);
  
  // Blink LED to signal reset
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  delay(1000);
  digitalWrite(LED_PIN, LOW);
}

void loop() {
  wdt_enable(WDTO_8S);

  // Infinite loop to simulate a crash
  while (1);
}

If the first instruction in the void setup() function sets the Watchdog timer to a bigger timeout like for example 8 seconds, the infinite bootloader loop can be prevented. For some strange reason wdt_disable() doesn't seem to work here.

References