Some notes on the Arduino Watchdog                                   Latest change 2017-02-09

ReactionsTo.jpg


In short:
This article addresses 2 problems:
 - How to find out whether your program started from a watchdog reset.
 - How to reset the arduino from your program.

A certain Arduino application which uses UDP communication across my network stalled sometimes for no known reason, and I had to power it down and on again to continue its functioning. So I decided to enable the watchdog such that it resets the Arduino when the communication failed for more than 8 seconds.
That worked well, but I also wanted to keep track of how often this happens. And then came a problem.
Below are the relevant parts in my program.

#define USEWATCHDOG
byte WDwakeUps;

// In the communication handler write somewhere:
 __asm__ ("wdr");  // kick watchdog

void setup()
{
  WDwakeUps = EEPROM.read(12);
  if((MCUSR & 0x08) == 0x08) // we woke up from watchdog reset
  {
    EEPROM.write(12, ++WDwakeUps);
    MCUSR &= ~0x08; // clear the WDRF flag
  }
#ifdef USEWATCHDOG
  cli();  // disable interrupts
  WDTCSR = 0x18; // Start timed sequence to change watchdog timeout
  WDTCSR = 0x29; // Enable watchdog for system reset after 8 seconds not kicked
  sei();  // re-enable interrupts
#endif

// other setup...
}

void loop(void)
{ // do something or nothing }

Now the watchdog reset worked fine but the check for waking up from watchdog reset did not, bit 3 in MCUSR was always zero.
Later I found out that in most? many? all? bootloaders this bit is inspected and if set, the wait time for expecting a new download is skipped. This surely makes sense, but the register is cleared too and that prevented my application to find out if it started from a watchdog reset.
And I did not want to dive into modifying the bootloader. What to do?

Well, the watchdog circuit can also give a special interrupt. In that interrupt I increment WDwakeUps in EEPROM and then do a system reset.

ISR(WDT_vect) // Watchdog interrupt handler
{
  EEPROM.write(12, ++WDwakeUps);
  __asm__ ("jmp 0"); // jump to the reset start address.
}

void setup()
{
  WDwakeUps = EEPROM.read(12);
#ifdef USEWATCHDOG
  cli();  // disable interrupts
  WDTCSR = 0x18; // Start timed sequence to change watchdog timeout
  WDTCSR = 0x61; // Enable watchdog for interrupt after 8 seconds not kicked
  sei();  // re-enable interrupts
#endif
// other setup...
}


Resetting the Arduino from your program:
In the example above the assembly instruction "jump to address 0" is used, the address where the processor always starts from reset. Now this way of resetting does not reset the hardware of peripherals like I/O ports and the like. If you need that, it takes an extra output pin and a hardware connection to the reset pin.
For example in an Arduino NANO-328 connect pin D2 to the reset pin and write:

ISR(WDT_vect) // Watchdog interrupt handler
{
  EEPROM.write(12, ++WDwakeUps);
  PORTC = 0;
  DDRC = 0x04; // make pin D2 an output so it pulls the reset pin low.
}