Watchdog Timer ISR(WDT_vect) and Serial.println

I am unable to get Serial.println to work when inside the ISR method triggered before a watchdog timeout. I have the optiboot bootloader installed. The watchdog is set to expire after 8 seconds and is set up to trigger both the interrupt and a reset. It appears the reset is working correctly because I see the "Finished Setup..." message repeatedly. Also, I have an LED attached to digital 6 and it lights up as indicated in the code, but for some reason Serial.println() does not work.

My original goal was to write some debug info to a MicroSD card but that wasn't working either so I tried with the Serial.println for simplicity.

Here is what I am seeing (I would have expected "Watchdog triggered" to be printed before the LED was lit)

Finished setup...
[pause]
[LED lights for 8 seconds]
Finished setup...
[pause]
[LED lights for 8 seconds]
Finished setup...
[pause]
[LED lights for 8 seconds]
Finished setup...
[pause]
[LED lights for 8 seconds]
Finished setup...
[pause]
[LED lights for 8 seconds]

#include <avr/wdt.h>

void enableWatchdog()
{
  cli();
  MCUSR &= ~(1<<WDRF);
  wdt_reset();
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = (~(1<<WDP1) & ~(1<<WDP2)) | ((1<<WDE) | (1<<WDIE) | (1<<WDP3) | (1<<WDP0));
  sei();
}

void disableWatchdog()
{
  cli();
  wdt_reset();
  MCUSR &= ~(1<<WDRF);
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = 0x00;
  sei();
}

ISR(WDT_vect)
{
  Serial.println("Watchdog triggered");
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  while(true);
}


void setup()
{
  disableWatchdog();
  enableWatchdog();
  Serial.begin(9600);
  Serial.println("Finished setup...");
}

void loop()
{
  while(true);
}

I suspect it would be easier to pursue the actual bug you are trying to fix rather than trying to create a postmortem debugger using the watchdog.

Yea, isn't that what Homer (of ancient Greek fame, not the Simpson Duh guy) asked, "Who will debug the debugger?" :wink:

zdynobeat:
I am unable to get Serial.println to work when inside the ISR...

You should not use Serial prints inside an ISR. It requires interrupts to work, and they are disabled inside an ISR.

Serial.println("Watchdog triggered");
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  while(true);

In particular the "while" there will stop it leaving the ISR and thus the serial prints will never appear.

But it will work, even with a endless while(true); loop in the ISR,
if you drop back to an Arduino version prior to 1.x since the output is polled in those releases.

--- bill

I originally coded the ISR to be a "catch-all" in the event an unexpected issue causes a lock-up, there is no actual issue to debug at the moment. I know nothing can be done if the offending code is located in the ISR.

bperrybap - I am using 1.0.1. I have seen plenty of watchdog examples which have a Serial.println in the ISR, they must have been written pre-1.x.

Nick - The "while" is in the ISR so the watchdog will timeout a second time and cause a reset. I remember reading somewhere that in order to get both the interrupt and the reset, I need to trigger the watchdog twice. Let me know if this isn't true or if there is a better alternative. I tested your claim of interrupts being disabled in the ISR and you're spot on. I altered the ISR as below and it works. The delay is needed to allow time for Serial.println before disabling interrupts again.

ISR(WDT_vect)
{
  detachInterrupt(1);
  detachInterrupt(0);
  sei();
  Serial.println("Watchdog triggered");
  delay(50);
  cli();
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  while(true);
}

I found this is also the reason the MicroSD doesn't work in the ISR. Is there a way to write to Serial/MicroSD without interrupts enabled? If not, what would be the best alternative?

I was thinking of writing to the EEPROM when in the ISR. Then in setup, read the EEPROM, write to MircoSD and continue on as normal. If anyone wondering why add the MicroSD to the mix, all other data is logged to it and I would like to have a central location.

Thanks everyone for the advice.

Writing to an SD card in the "normal" execution path and in the context of an interrupt service routine is a good way to corrupt any open files or possibly the filesystem.

I was thinking of writing to the EEPROM when in the ISR. Then in setup, read the EEPROM, write to MircoSD and continue on as normal.

...is an excellent choice. I worked with a device that did just that. The code was simple, effective, and easy to debug.

ISR(WDT_vect)

{
  detachInterrupt(1);
  detachInterrupt(0);
  sei();
  Serial.println("Watchdog triggered");
  delay(50);
  cli();
  pinMode(6, OUTPUT);
  digitalWrite(6, HIGH);
  while(true);
}

I wouldn't be enabling interrupts personally. If the watchdog fires, something has gone wrong. Enabling interrupts (and doing things like serial prints) might mean more things will go wrong.

According to the datasheet there are various modes including "Interrupt and System Reset Mode". The documentation is a bit vague about that mode, but it looks as if, if the ISR is not entered before the second watch dog time-out, it resets. In other words, you might have 1 second to enter the ISR, if you don't (eg. if interrupts are disabled) then it resets. I don't know if your technique of doing an endless loop will count, because it is already in the ISR. (I might be wrong about that, it makes more sense for the watchdog to save itself it the ISR goes into a loop for some reason).

Sorry about the long wait. It was a holiday weekend in the United States.

I found the web page I referenced earlier which contains details on how the watchdog works. This is why I have the infinite loop in the ISR.
http://www.atmel.com/Images/doc2551.pdf
page 5

The infinite loop at the end of the interrupt handler prevents the main code from potentially causing more damage.

I seem to need it to trigger the reset as well. I have a working example writing to the EEPROM in the ISR and then printing the data using Serial.println on startup. I think I have everything I need to move on. Thanks once again for everyone's advice.

#include <avr/wdt.h>
#include <EEPROM.h>

byte check = 0;
byte value = 0;

/* sets the watchdog timer both interrupt and reset mode with an 8 second timeout */
void enableWatchdog()
{
  cli();
  MCUSR &= ~(1<<WDRF);
  wdt_reset();
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = (~(1<<WDP1) & ~(1<<WDP2)) | ((1<<WDE) | (1<<WDIE) | (1<<WDP3) | (1<<WDP0));
  sei();
}

/* disables the watchdog timer */
void disableWatchdog()
{
  cli();
  wdt_reset();
  MCUSR &= ~(1<<WDRF);
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = 0x00;
  sei();
}

/* this is called when the watchdog times out and before the reset */
ISR(WDT_vect)
{
  randomSeed(analogRead(0)); // leave analog 0 unconnected (http://arduino.cc/en/Reference/Random)
  value = random(0,255);     // just a meaningless random number to retrieve later
  EEPROM.write(1, value);    // write the random number to the second byte
  EEPROM.write(0,1);         // write a "1" to the first byte to indicate the data in second byte is valid and the ISR triggered properly
  while(true);               // triggers the second watchdog timeout for a reset
}


void setup()
{
  disableWatchdog();
  enableWatchdog();
  Serial.begin(9600);
  check = EEPROM.read(0);
  
  // if the watchdog triggered and the ISR completed, the first EEPROM byte will be a "1"
  if(check == 1)
  {
    value = EEPROM.read(1);
    EEPROM.write(0,0); // reset byte so the EEPROM is not read on next startup
    Serial.print("Watchdog was triggered and the following was read from EEPROM: ");
    Serial.println(value);
  }
  
  Serial.println("Finished setup...");
}

void loop()
{
  while(true);
}