Arduino/Atmega328PU wake up with a long button press

Hello there,

I’m working on a new project. One important topic is to save power in standalone Atmega 328PU. The microcontroller is into a deep sleep mode and should only wake up after the button was pressed for over 3 seconds. I wrote the following two codes.

#define LED_PIN 13
void setup() {
// put your setup code here, to run once:
pinMode(LED_PIN, OUTPUT);
//Save Power by writing all Digital I0 LOW 
for(int i=0; i<20; i++){
  if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  attachInterrupt(0, digitalInterrupt, FALLING); //interrupt for waking up
}

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);

//DISABLE ADC
ADCSRA &= ~(1<<7);
//ENABLE SLEEP
SMCR |= (1<<2); //power down mode
SMCR |= 1; //enable sleep

//BOD DISABLE
MCUCR |=(3 << 5); //set both BODS and BODSE at the same time
MCUCR = (MCUCR & ~(1 << 5)) | (1<< 6); //then set the BODS bit and clear the BODS bit at the same time
__asm__ __volatile__("sleep");
}

void digitalInterrupt(){
  //needed for the digital input interrupt
}
int inPin = 2;  // the pin number for input (for me a push button)
int ledPin = 13; 

int current;         // Current state of the button
                     // (LOW is pressed b/c i'm using the pullup resistors)
long millis_held;    // How long the button was held (milliseconds)
long secs_held;      // How long the button was held (seconds)
byte previous = HIGH;
unsigned long firstTime; // how long since the button was first pressed 


void setup() {
  pinMode(ledPin, OUTPUT);
  digitalWrite(inPin, HIGH);  // Turn on 20k pullup resistors to simplify switch input
}

void loop() {
  current = digitalRead(inPin);

  // if the button state changes to pressed, remember the start time 
  if (current == LOW && previous == HIGH) {
    firstTime = millis();
  }

  millis_held = (millis() - firstTime);
  secs_held = millis_held / 1000;

    if (current == LOW && previous == LOW) {

      // If the button was held for more then 3 seconds blink LED 1 time
      if (secs_held >= 3) {
        ledblink(1,4000,ledPin); 
      }

    }
  

  previous = current;

}

// Just a simple helper function to blink an led in various patterns
void ledblink(int times, int lengthms, int pinnum){
  for (int x=0; x<times;x++) {
    digitalWrite(pinnum, HIGH);
    delay (lengthms);
    digitalWrite(pinnum, LOW);
    delay(lengthms);
  }
}

The first code is for the deep sleep mode and wakes the Atmega 328PU up after an external interrupt.
The second code is a normal code (without any power saving option) and reads the digital input and sets a timer if the button was pressed. If the timer is over 3 seconds and the button is still pressed something will happen (for example a LED will flash).

So now my question is how to combine them? Is there any way to do that?
I hope somebody can help me :slight_smile: I’m looking forward to your replies.

Best regards

Maurice W.

You could use the interrupt to wake the processor on the switch closure. Then set up a watchdog timer. The closest options are 2 seconds and 4 seconds. You would then change the interrupt to detect a switch open. Then find if the watchdog fired first, or the switch open fired first. If you really want 3 seconds have a watchdog of 2 seconds followed by a watchdog of 1 second.

http://gammon.com.au/power

Hello Nick, thanks for your quick reply. Your idea sounds good. But where do I have to set the watchdog timer in my code and how can I detect a switch open?

My link shows how to set up the watchdog timer. To detect a switch open you make an interrupt on RISING rather than FALLING (or vice-versa depending on how the switch is wired).