WDT periodic WakeUp and External Interrupt problem

I need to run two separate interrupt functions.
The first function is a Sleep/Wake using the WDT.
The second function is the use of an External Interrupt that allows for both a Short button press and a Long button press.
Both interrupt functions must Wake the processor, fulfill a task, and go back to Sleep.
I am attempting to combine two examples:

When I run the attached code, with all the "Button Stuff" commented out, the WDT works great and Wakes the micro as expected:

The Log is:

Initialising...
Initialisation complete.
Awake....
sleepCount: 1
08:06:20.687 -> Going to Sleep...
Awake....
sleepCount: 2
08:06:30.844 -> Going to Sleep...
Awake....
sleepCount: 3
08:06:41.138 -> Going to Sleep...

When I run the attached code, with all the "Button Stuff" uncommented, the WDT only Wakes the micro once.
However, the Short button press and the Long button press are detected.
The Log is:

Initialising...
Initialisation complete.
Initializing Button pin
08:10:11.529 -> Button pin initialized
08:10:11.575 -> Awake....
sleepCount: 1
Short press has been detected
Long press has been detected
Short press has been detected
Long press has been detected

I have been banging my head as to what I am doing incorrectly.
Please could someone point-out my error(s)

WDT-BUTTON-TEST-01.txt (9.74 KB)

Sorry text file missing "/*" at the start.

I'm really not sure if I understand your problem correctly, but the posted button code includes some while(1) loops which will cause the WDT/sleep code to be defunct. Your loop() code does not do any checking whether the wakeup was caused by WDT or button and that is a problem too.

Danois90:
I'm really not sure if I understand your problem correctly

I cannot get both WDT periodic Wake AND the Interrupt to work together in the same sketch.

the posted button code includes some while(1) loops which will cause the WDT/sleep code to be defunct.

How do I overcome this so that the WDT/sleep code keeps running?

Your loop() code does not do any checking whether the wakeup was caused by WDT or button and that is a problem too.

I have changed the loop code as below.
if (f_wdt == 1) { // WOKE UP FROM WDT
if (f_wdt != 1) { // WOKE UP FROM BUTTON PRESS
loop code:

void loop() {

  if (f_wdt == 1) { // WOKE UP FROM WDT
    Serial.println("Awake....");
    delay(100);
    sleepCount++;
    Serial.print("sleepCount: ");
    Serial.println(sleepCount);

    // 10.27 seconds / Sleep Cycle (59 = ~10Min)
    if (sleepCount >= 59) {
      // Toggle the LED on/off every 10 minutes heartbeat
      digitalWrite(LED_PIN, 1);
      // wait
      delay(20);
      // Toggle the LED off
      digitalWrite(LED_PIN, 0);

      Serial.println("Got Hearbeat");
      delay(100);
    }

    if (sleepCount >= 88) {
      Serial.println("Got 88 - 15 Minutes");
      delay(100);
      sleepCount = 0;
    }
  } // END if (f_wdt == 1) {

  //  // Wait until the watchdog has triggered a wake up.
  //  if (f_wdt != 1) {
  //    return;
  //  }

      // clear the flag so we can run above code again after the MCU wake up
      f_wdt = 0;

  //************************************************************
  // Button Stuff
  int longButton = 0;
  int count = 0;

  if (f_wdt != 1) { // WOKE UP FROM BUTTON PRESS
    while (true) {
      switch (resultButton) {

        case STATE_NORMAL: {
            /*  Serial.print(".");
              count++;
              count = count % 10;
              if (count==0) Serial.println(""); */
            break;
          }

        case STATE_SHORT: {
            Serial.println("Short press has been detected");
            // NOW SWITCH LCD ON
            digitalWrite(LED_PIN, HIGH);
            delay(1000);
            digitalWrite(LED_PIN, LOW);
            enterSleep(); // Re-enter sleep mode.
            resultButton = STATE_NORMAL;
            enterSleep(); // Re-enter sleep mode.
            break;
          }

        case STATE_LONG: {
            Serial.println("Long press has been detected");
            // NOW GET SETUP
            digitalWrite(LED_PIN, HIGH);
            delay(3000);
            digitalWrite(LED_PIN, LOW);
            longButton++;
            resultButton = STATE_NORMAL;
            enterSleep(); // Re-enter sleep mode.
            break;
          }
      }
      if (longButton == 5) {
        Serial.println("Halting");
        HALT;
      }
    } // END while (true)
  } // END if (f_wdt != 1)
  //************************************************************


  // Re-enter sleep mode.
  enterSleep();

} // END loop

This is driving me nuts :slight_smile:

"while (true)" in button handling will cause that while loop to run forever and "enterSleep()" should not be called in the button code since it is correctly called at the end of "loop()".

I have commented the following out in loop:
// while (true) {
// enterSleep(); // Re-enter sleep mode.

How I have the following problems:
Before any button press, the WDT wakes correctly.
Once a button press occurs, ONLY a Short Press is detected, irrespective if it is a Long or Short press.
The WDT seems to run, but I do not get the: " Serial.println("Awake....");" in loop.

The loop code:

void loop() {

  if (f_wdt == 1) {
    Serial.println("Awake....");
    delay(100);
    sleepCount++;
    Serial.print("sleepCount: ");
    Serial.println(sleepCount);

    // 10.27 seconds / Sleep Cycle (59 = ~10Min)
    if (sleepCount >= 59) {
      // Toggle the LED on/off every 10 minutes heartbeat
      digitalWrite(LED_PIN, 1);
      // wait
      delay(20);
      // Toggle the LED off
      digitalWrite(LED_PIN, 0);

      Serial.println("Got Hearbeat");
      delay(100);
    }

    if (sleepCount >= 88) {
      Serial.println("Got 88 - 15 Minutes");
      delay(100);
      sleepCount = 0;
    }
  } // END if (f_wdt == 1) {

  //  // Wait until the watchdog has triggered a wake up.
  //  if (f_wdt != 1) {
  //    return;
  //  }

  // clear the flag so we can run above code again after the MCU wake up
  f_wdt = 0;

  //************************************************************
  // Button Stuff
  int longButton = 0;
  int count = 0;

  if (f_wdt != 1) {
    //    while (true) {
    switch (resultButton) {

      case STATE_NORMAL: {
          /*  Serial.print(".");
            count++;
            count = count % 10;
            if (count==0) Serial.println(""); */
          break;
        }

      case STATE_SHORT: {
          Serial.println("Short press has been detected");
          // NOW SWITCH LCD ON
          digitalWrite(LED_PIN, HIGH);
          delay(1000);
          digitalWrite(LED_PIN, LOW);
          enterSleep(); // Re-enter sleep mode.
          resultButton = STATE_NORMAL;
          //            enterSleep(); // Re-enter sleep mode.
          break;
        }

      case STATE_LONG: {
          Serial.println("Long press has been detected");
          // NOW GET SETUP
          digitalWrite(LED_PIN, HIGH);
          delay(3000);
          digitalWrite(LED_PIN, LOW);
          longButton++;
          resultButton = STATE_NORMAL;
          //            enterSleep(); // Re-enter sleep mode.
          break;
        }
    }
    if (longButton == 5) {
      Serial.println("Halting");
      HALT;
    }
    //    } // END while (true)
  } // END if (f_wdt != 1)
  //************************************************************

  // clear the flag so we can run above code again after the MCU wake up
  f_wdt = 0;

  // Re-enter sleep mode.
  enterSleep();

}

The Log:

Initialising...
Initialisation complete.
Initializing Button pin
15:20:26.456 -> Button pin initialized
15:20:26.503 -> Awake....
sleepCount: 1
15:20:26.549 -> Going to Sleep...
Awake....
sleepCount: 2
15:20:36.723 -> Going to Sleep...
Awake....
sleepCount: 3
15:20:47.023 -> Going to Sleep...

PUSHED BUTTON HERE (SHORT PRESS)

Going to Sleep...
Short press has been detected
Going to Sleep...
Going to Sleep...

In loop you set "f_wdt = 0" and right after you have a "if (f_wdt != 1)". You should change the name of "f_wdt" to something like "wake_cause" and have it being of type volatile byte initialized to "0". In the watchdog interrupt set it to "1" and in the button interrupt set it to "2". In loop() you can check for a watchdog (1) or a button press (2). Just before you enter sleep again, you set it to "0" to start over.

Thanks, I have done the changes and am now not detecting the Long press of the button.
When the button is pressed for a long press (>4000), a Short press is detected.
While the button is pressed, the WDT still runs, I don't know is this could be the problem.

loop code:

// main loop
void loop() {

  if (wake_cause == 1) {
    Serial.println("Awake....");
    delay(100);
    sleepCount++;
    Serial.print("sleepCount: ");
    Serial.println(sleepCount);

    // 10.27 seconds / Sleep Cycle (59 = ~10Min)
    if (sleepCount >= 59) {
      // Toggle the LED on/off every 10 minutes heartbeat
      digitalWrite(LED_PIN, 1);
      // wait
      delay(20);
      // Toggle the LED off
      digitalWrite(LED_PIN, 0);

      Serial.println("Got Hearbeat");
      delay(100);
    }

    if (sleepCount >= 88) {
      Serial.println("Got 88 - 15 Minutes");
      delay(100);
      sleepCount = 0;
    }
  } // END if (wake_cause == 1) {


  //************************************************************
  // Button Stuff
  int longButton = 0;
  int count = 0;

  if (wake_cause == 2) {
    //    while (true) {
    switch (resultButton) {

      case STATE_NORMAL: {
          /*  Serial.print(".");
            count++;
            count = count % 10;
            if (count==0) Serial.println(""); */
          break;
        }

      case STATE_SHORT: {
          Serial.println("Short press has been detected");
          // NOW SWITCH LCD ON
          digitalWrite(LED_PIN, HIGH);
          delay(1000);
          digitalWrite(LED_PIN, LOW);
          resultButton = STATE_NORMAL;
          break;
        }

      case STATE_LONG: {
          Serial.println("Long press has been detected");
          // NOW GET SETUP
          digitalWrite(LED_PIN, HIGH);
          delay(3000);
          digitalWrite(LED_PIN, LOW);
          longButton++;
          resultButton = STATE_NORMAL;
          //            enterSleep(); // Re-enter sleep mode.
          break;
        }
    }
    if (longButton == 5) {
      Serial.println("Halting");
      HALT;
    }
    //    } // END while (true)
  } // END if (wake_cause != 1)
  //************************************************************

  // clear the flag so we can run above code again after the MCU wake up
  wake_cause = 0;

  // Re-enter sleep mode.
  enterSleep();

}
volatile byte wake_cause = 0;

ISR(WDT_vect) {
  if (wake_cause == 0) {
    wake_cause = 1;
  }
}
void checkButton() {
  wake_cause = 2;

Log:

Initialising...
Initialisation complete.
Initializing Button pin
16:24:47.030 -> Button pin initialized
16:24:47.030 -> Going to Sleep...
Awake....
sleepCount: 1
16:24:57.310 -> Going to Sleep...
Awake....
sleepCount: 2
16:25:07.582 -> Going to Sleep...
Awake....
sleepCount: 3
16:25:17.842 -> Going to Sleep...

PRESSED BUTTON (SHORT PRESS)

Going to Sleep...
Short press has been detected
Going to Sleep...
Awake....
sleepCount: 4
16:25:28.125 -> Going to Sleep...
Awake....
sleepCount: 5
16:25:38.428 -> Going to Sleep...
Awake....
sleepCount: 6
16:25:48.707 -> Going to Sleep...
Awake....
sleepCount: 7
Going to Sleep...
Awake....
sleepCount: 8
16:26:09.248 -> Going to Sleep...
Awake....
sleepCount: 9
16:26:19.543 -> Going to Sleep...

PRESSED BUTTON (LONG PRESS)

Going to Sleep...
Awake....
sleepCount: 10
16:26:29.811 -> Going to Sleep...
Short press has been detected
Going to Sleep...
Awake....
sleepCount: 11
16:26:40.085 -> Going to Sleep...
Awake....
sleepCount: 12
16:26:50.398 -> Going to Sleep...
Awake....
sleepCount: 13
16:27:00.674 -> Going to Sleep...

The button code uses millis(), during sleep / power down millis will not increment. The button code must be changed to prevent sleep during button handling.

I have moved all the button code into a separate function "getButton()":

void getButton() {
  const unsigned long LONG_DELTA = 4000ul;               // hold seconds for a long press
  const unsigned long DEBOUNCE_DELTA = 30ul;        // debounce time
  static int lastButtonStatus = HIGH;                                   // HIGH indicates the button is NOT pressed
  int buttonStatus;                                                                    // button atate Pressed/LOW; Open/HIGH
  static unsigned long longTime = 0ul, shortTime = 0ul; // future times to determine is button has been poressed a short or long time
  boolean Released = true, Transition = false;                  // various button states
  boolean timeoutShort = false, timeoutLong = false;    // flags for the state of the presses

  buttonStatus = digitalRead(BUTTON_PIN);                // read the button state on the pin "BUTTON_PIN"
  timeoutShort = (millis() > shortTime);                          // calculate the current time states for the button presses
  timeoutLong = (millis() > longTime);

  if (buttonStatus != lastButtonStatus) {                          // reset the timeouts if the button state changed
    shortTime = millis() + DEBOUNCE_DELTA;
    longTime = millis() + LONG_DELTA;
  }

  Transition = (buttonStatus != lastButtonStatus);        // has the button changed state
  Released = (Transition && (buttonStatus == HIGH)); // for input pullup circuit

  lastButtonStatus = buttonStatus;                                     // save the button status

  if ( ! Transition) {                                                                //without a transition, there's no change in input
    // if there has not been a transition, don't change the previous result
    resultButton =  STATE_NORMAL | resultButton;
    return;
  }

  if (timeoutLong && Released) {                                      // long timeout has occurred and the button was just released
    resultButton = STATE_LONG | resultButton;       // ensure the button result reflects a long press
  } else if (timeoutShort && Released) {                          // short timeout has occurred (and not long timeout) and button was just released
    resultButton = STATE_SHORT | resultButton;     // ensure the button result reflects a short press
  } else {                                                                                  // else there is no change in status, return the normal state
    resultButton = STATE_NORMAL | resultButton; // with no change in status, ensure no change in button status
  }
}

I have changed the button ISR to call this function:

void checkButton() {
  wake_cause = 2;
  getButton();
}

I am having the same problem in that I am unable to read the Long button press.
I thought that moving the button code with millis() would overcome this.

No other interrupts will be executed whilst inside an interrupt. millis() is based on a timer interrupt so it is unusable in an interrupt handler. You must use a different approach if your code is supposed to work. Look at this pseude-code:

volatile byte wake_cause;

void button_interrupt()
{
  if (wake_cause == 0) wake_cause = 1;
}

void watchdog_interrupt()
{
  if (wake_cause == 0) wake_cause = 2;
}

void handle_watchdog()
{
  //Whatever
}

void handle_button()
{
  unsigned long ms = millis(), elapsed = 0;
  while ((digitalRead(button) == LOW) && (elapsed < LONG_PRESS_MILLIS))
  {
    elapsed = millis() - ms;
    delay(10);
  }
  if (elapsed >= LONG_PRESS_MILLIS)
  {
    //Long press detected
  }
  else if (elapsed >= SHORT_PRESS_MILLIS)
  {
    //Short press detected
  }
}

void setup()
{
  //setup watchdog
  //setup button
}

void loop()
{
  wake_cause = 0;
  enable_watchdog();
  go_to_sleep();
  disable_watchdog();

  if (wake_cause == 1) handle_button();
  else if (wake_cause == 2) handle_watchdog();
}

It is as simple as that.

@Danois90
Many Thanks - Sorted.
I will post my code once I have cleaned it up if others find it useful.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.