Proper way to use ISR then System Reset with watch dog timer on mega 2560

Hi,

seems im having some trouble with using a watch dog timer on my 2560.

When i test the sketch below on a Uno it works as expected > the sketch is loaded > WDT is set to fire an interrupt > the interrupt disables WDT, writes some values to EEPROM (i read it is bad practice to read/write to EEPROM both in main application and within interrupts but im not sure how else to do this...) then enables the watchdog system reset while im assuming the timeout flag is still set so the arduino immediately resets.

on a Uno you can see that the "cycles" variable starts from 0 and counts up, after a WD reset it starts from 0 and the process is repeated indefinitely. however on a Mega, after a reset the sketch doesnt start. im assuming the WDT is still set and the Mega's bootloader is not disabling or resetting it so it immediately bites before any instructions are processed.

my question is: am i doing this correctly and just hitting a stale mate due to hardware/software limitations (eg. wrong bootloader). or is there another more portable way to accomplish this behavior that i am not aware of?

i have tried using the line:
asm volatile (" jmp 0");
at the end of the ISR, though this seems to start the sketch over, it is by far not a true system reset.

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

int RED = 13;          // the pin that the LED is attached to
int GREEN = 6;          // the pin that the LED is attached to
int BLUE = 10;           // the pin that the LED is attached to

unsigned long cycles = 0; //number of iteration cycles from start

const int maxResets = 5; //number of times the mcu can reset due to fault before led illuminates

const int error_code_address = 0; 
byte error_code = EEPROM.read(error_code_address); //if WDT resets this will be = 1, if its a clean user reset the val = 0

const int reset_cnts_address = 1;
byte reset_cnts = EEPROM.read(reset_cnts_address); //number of contiguous WDT resets sinces last clean start

const int ctr_address = 2;
unsigned long nonvol_counter = 0; //nonvolatile counter, after WDT resets this counter will load its last value, or 0 if clean start

ISR (WDT_vect)
{
  wdt_disable(); //disable the watchdog    
  EEPROM.write(error_code_address, 1); //there was an error and WDT needs to reset the system. so write 1 to EEPROM so arduino knows after it resets
  EEPROM.updateLong(ctr_address, nonvol_counter); //since there was an error we dont want to loose this value so write it to EEPROM
  reset_cnts = constrain(reset_cnts + 1, 0, 255 ); //one byte in EEPROM stores this var, but we also dont want it do rollover.
  EEPROM.write(reset_cnts_address, reset_cnts); //wire the incrimented reset counts to EEPROM so arduino knows after it resets
  digitalWrite(BLUE, 0); 
  digitalWrite(RED, 1);
  //WDTCSR = bit (WDE); //"Watchdog System Reset Enable" THIS WORKS ON UNO BUT NOT ON MEGA
  asm volatile (" jmp 0"); //THIS WORKS ON BOTH BUT IS NOT A FULL RESET
}  // end

void setup()
{
  pinMode(RED, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(GREEN, OUTPUT);
  digitalWrite(RED, 0);
  digitalWrite(BLUE, 1);
  digitalWrite(GREEN, 0);
  // clear various "reset" flags
  MCUSR = 0;
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  
  if (error_code != 0){ //if there was an error from last run
    EEPROM.write(error_code_address, 0); //reset the error code and clean things up
    nonvol_counter = EEPROM.readLong(ctr_address); //reload the counter since the arduino crashed
    if (reset_cnts >= maxResets) { //check to see how many times in a row the arduino has crashed
      digitalWrite(GREEN, 1); //if the arduino crash count exceeds the max allowed lets indicate this to user
    }
  } else { //clean system start, no errors from last run
    nonvol_counter = 0; //start counter from 0/clean
    EEPROM.write(reset_cnts_address, 0); //since this was clean, we reset the error counter 
    reset_cnts = 0; //and update the variable in SRAM
  }
  
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Serial.println("\n\n\nSETUP DONE!\n\n\n");
  delay(500);
  wdt_reset();
}

void loop()
{
  nonvol_counter++;
  
  Serial.print( "Sketch Cycles:" );  
  Serial.print( cycles );
  Serial.print( " nonvolatile Counter:" );  
  Serial.print( nonvol_counter );
  Serial.println();
  delay(1);
  
  //wdt_reset();  // pat the dog //for testing this is disabled
  
  cycles++;
}

Please help :slight_smile:

Does the Arduino run setup() after a WDT interrupt?

If so why not just write a variable in the WDT ISR and then read that value in setup() to decide whether a WDT event has occurred.

...R

Hi Robin,
I added a print statement to the above code that indicates when setup has been run, and i do see it every time it resets while using the method:
asm volatile (" jmp 0");
but from what i have read this is not a full system reset and some variables may still be initialized.
maybe its not necessary for me to do a full reset in my application and this may suffice.
but could someone please explain the difference between a WDT reset verses just jumping to line 0???

Thanks

I hope you haven't put the print statement in the ISR?

Have you read the Atmel datasheet? It should explain all of the details about the WDT.

...R

Robin2:
I hope you haven't put the print statement in the ISR?

Absolutely not! the print statement is at the end of the setup() function.

Robin2:
Have you read the Atmel datasheet? It should explain all of the details about the WDT.

I have been using this for the most part (page 54):

That's the datasheet I have. What do you not understand? (I don't want to read more than necessary).

...R

Its not that i dont understand (though i say that lightly), its more of a situation where i wrote a sketch that worked on the Uno but causes the Mega to lock up after restart. And this project requires me to use a Mega..

specifically the last line of this ISR works as desired on the Uno, but not the Mega (well it resets the Mega but then nothing..):

ISR (WDT_vect)
{
  wdt_disable(); //disable the watchdog    
  EEPROM.write(error_code_address, 1); //there was an error and WDT needs to reset the system. so write 1 to EEPROM so arduino knows after it resets
  EEPROM.updateLong(ctr_address, nonvol_counter); //since there was an error we dont want to loose this value so write it to EEPROM
  reset_cnts = constrain(reset_cnts + 1, 0, 255 ); //one byte in EEPROM stores this var, but we also dont want it to rollover.
  EEPROM.write(reset_cnts_address, reset_cnts); //write the incrimented reset counts to EEPROM so arduino knows after it resets
  digitalWrite(BLUE, 0); 
  digitalWrite(RED, 1);
  WDTCSR = bit (WDE); //"Watchdog System Reset Enable" THIS WORKS ON UNO BUT NOT ON MEGA
}  // end

while this works on both the Uno and Mega but my research indicated that " jmp 0" is not a true reset.

ISR (WDT_vect)
{
  wdt_disable(); //disable the watchdog    
  EEPROM.write(error_code_address, 1); //there was an error and WDT needs to reset the system. so write 1 to EEPROM so arduino knows after it resets
  EEPROM.updateLong(ctr_address, nonvol_counter); //since there was an error we dont want to loose this value so write it to EEPROM
  reset_cnts = constrain(reset_cnts + 1, 0, 255 ); //one byte in EEPROM stores this var, but we also dont want it to rollover.
  EEPROM.write(reset_cnts_address, reset_cnts); //write the incrimented reset counts to EEPROM so arduino knows after it resets
  digitalWrite(BLUE, 0); 
  digitalWrite(RED, 1);
  asm volatile (" jmp 0"); //THIS WORKS ON BOTH BUT IS NOT A FULL RESET
}  // end

Im trying to utilize the "interrupt then system reset" functionality of the WDT.
as of now im a little confused as to what the major differences between "WDT reset" and " jmp 0" are??
however i have read that installing a modified bootloader on the Mega could solve the issue of the WDT not being disabled/reset upon bootloader start.

so if my only option is to replace the bootloader and being that i have never worked with bootloaders, im a little stumped as to where to get the proper bootloader and how to go about loading it? I do have an ISP if that helps.

thanks again Robin.

The document you quoted is for the 328 in the Uno, not the 2560 in the Mega. Maybe there are differences - it seems quite probable.

Edit to add ...

I had assumed you were just using the WDT to restart the Arduino if it crashed - but that's clearly not what you are doing. Can you explain what you are trying to achieve?

...R

Seems odd that it could perform a reset but not restart afterwards. Can you add some judicious digitalWrite calls to turn LEDs on and off and use that to track down how far it gets through the normal reset sequence?

The mega boards original came with an older bootloader that did not probably handle watchdog timer interrupts correctly (it wouldn't disable WDT interrupt, so easy to get stuck in endless loop of bootloader code getting interrupted back to bootloader).

The mega bootloader they now distribute with the current IDE handles WDT interrupts correctly and is named stk500v2/stk500boot_v2_mega2560.hex and is now the default bootloader that will be used if you burn the bootloader from the IDE.

As you seem to own a Uno board it's not a hard procedure to use it running the arduinoISP sketch to burn this newer bootloader onto your mega board. And this is the proper solution if you ever plan on using the WDT on your mega board. Jumping to zero has problems because it doesn't initialize any of the peripheral hardware, pin modes, etc so will tend to byte you in the ass sooner or later.