Keeping time in sleep mode

I’m experimenting with a 328p in advance of a project where I want the MC to wake up every 24 hours, do something, and go back to sleep to save power. The MC is on a breadboard with a 16Mhz crystal and 22pF caps. The bootloader is just that of the standard Arduino Uno. My experimental code is below based on [Nick Gammon’s power tutorial. It simply turns on an LED for 2 seconds, then sleeps for about 8 seconds using the watchdog timer.

However, each cycle is taking about 11.25 seconds, meaning the sleep cycle is about 9.25 seconds. I’ve tried different sleep modes but it doesn’t seem to make any difference. Is there some way to make the watchdog timer more accurate, or reference the crystal during sleep? Or am I missing something obvious?

void loop(void) {
  // Light the LED
  pinMode(8, OUTPUT);
  digitalWrite(8, HIGH);
  delay(2 * 1000);
  digitalWrite(8, LOW);
  // go to sleep:

  // 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
  wdt_reset();  // pat the dog
  set_sleep_mode (SLEEP_MODE_PWR_SAVE);  
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  sleep_cpu ();  
  // cancel sleep as a precaution

](Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors)

The watchdog timer is inaccurate, not adjustable and is not meant for timekeeping.

The CPU clock does not run in sleep mode. However, it is possible to have a 32kHz watch crystal running Timer2 in asynchronous mode, and keep time with that even in the “deepest” sleep modes. In that case the CPU can run using the built-in 8 MHz RC oscillator when awake. You will have to replace the 16 MHz crystal on your board with a watch crystal in order to experiment with that mode (and be sure to set the fuses for LP Xtal and have the CPU use the RC oscillator). I posted some code for this here: (see reply #9)

Thank you! I was expecting someone would probably say the WDT wasn’t designed for this. I wasn’t expecting a nice workaround. I’ll check it out.