Do not listen to inputs for X seconds

Hi all,

For my battery project I want to detect humans in a room. For this I use a PIR with an atMega328P.
Though I know the current consumption can be better, I’m quite satisfied on the result so far.
Somehow I’m not achieving in lowering the power consumption.
The setup consumes 0.04mA in sleep mode, and 7mA while on (with one LED).

As you may find in my code, when the PIR is active, the LED goes ON and stays on for 30seconds. It is a single shot trigger.
After this 30s, I want the PIR to check again if there is anyone in the room, and loop this continuous.

However, I’ve noticed I could lower the power consumption if the atMega328 directly goes to sleep after the LED is turned on(current consumption would be around 4mA only then). When the LED is triggered, it will stay on for 30seconds, nothing can change that. So the atMega can go to sleep.
I’m having a hard time to make this work.

Does anyone know how to not listen to any input for X seconds

#include <LowPower.h>
#include <avr/sleep.h>

const byte led_pin = 13;
const byte interrupt_pin = 2;
volatile byte state = LOW;



void wake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (0);
}  // end of wake

void setup() {
  Serial.begin(9600);
  pinMode(led_pin,OUTPUT);
  pinMode(interrupt_pin, INPUT);
  digitalWrite(2, HIGH); 

    for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  //Disable ADC - don't forget to flip back after waking up if using ADC in your application ADCSRA |= (1 << 7);
  ADCSRA &= ~(1 << 7);
  
  
  //ENABLE SLEEP - this enables the sleep mode
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
  
  //DISABLE BOD
  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 BODSE bit at the same time

  //SET CLOCK PROCESSOR
  CLKPR = 0x80;
  CLKPR = 0x01;

  for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }
}

void loop() {

  ADCSRA = 0;
  
//  attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine,RISING);
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
  delay (5000);
  attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine,RISING);
  detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt

   
  // the usual wake routine that turns on the LED
  if (state==HIGH){
    digitalWrite(led_pin,HIGH);
    delay(30000);
  }
  if (state==HIGH){
    state = LOW;
    digitalWrite(led_pin,LOW);
    delay(100);
  }

}

void interrupt_routine(){
  state = HIGH;
}

Record the time at which you got the last input using millis() function.
If you get another input, but the elapsed time is less than your threshold (X seconds), ignore it.

uint32_t time_now;
uint32_t event_time;

time_now = millis();
if (time_now - event_time >= X * 1000)
{
     event_time = time_now();
     //handle event
}
// else ignore event

Hi pcbbc,

Thank you, but I'm not sure how to implement it, sorry.

I want to make sure, as soon as the LED goes HIGH I start the timer.

I have it like this currently, but can’t get it to work

#include <LowPower.h>
#include <avr/sleep.h>

const int transistor = 13;
const byte interrupt_pin = 2;
volatile byte state = LOW;

uint32_t time_now;
uint32_t event_time;

void wake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (0);
}  // end of wake

void setup() {
  Serial.begin(9600);
  pinMode(transistor, OUTPUT);
  pinMode(interrupt_pin, INPUT);
  digitalWrite(2, HIGH); 

    for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  //Disable ADC - don't forget to flip back after waking up if using ADC in your application ADCSRA |= (1 << 7);
  ADCSRA &= ~(1 << 7);
  
  
  //ENABLE SLEEP - this enables the sleep mode
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
  
  //DISABLE BOD
  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 BODSE bit at the same time

  //SET CLOCK PROCESSOR
  CLKPR = 0x80;
  CLKPR = 0x01;
}

void loop() {
  ADCSRA = 0;
  time_now = millis();
  
if (time_now - event_time >= 30 * 1000){
  event_time = time_now();
  // the interrupt must be attached each loop
  attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine,RISING);
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
  detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt
  Serial.print("HALLO");
  Serial.println();
   
  // the usual wake routine that turns on the LED
  if (state==HIGH){
    digitalWrite(transistor,HIGH);
    delay (100);
    attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine,RISING);
    LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
    detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt
    delay(3000);  
  }
}
else if (state==HIGH){
    state = LOW;
    digitalWrite(transistor,LOW);
    ADCSRA = 0;
    delay(100);
    attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine,RISING);
    LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
    detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt
    }

}

void interrupt_routine(){
  state = HIGH;
}

It's the interrupt you want to ignore for 30 seconds right?

void interrupt_routine(){
  time_now = millis();
  if (time_now - event_time >= X * 1000)
  {
     event_time = time_now;
     state = HIGH;
  }
}

Now state will only go HIGH at most every 30 seconds.

Then in loop, if you want to sleep until an event...

while (STATE == LOW)
{
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
}
STATE = LOW;

Keep the interrupt enabled permanently. It will use very little power if you wake up and go back to sleep immediately.

Also my apologies. Earlier I wrote...

event_time = time_now();

when I meant...

event_time = time_now;

Aha, yes it does make sense why you put that code there instead of in the loop.
Though I was hopeful this breaks the code, the LED won’t turn on anymore when there is motion.
The current consumes more though when there is motion, obviously. So the interrupt does still do something, would be nice to close it completely, if possible.

Here is my implementation, would you mind to have another look? I probably did something wrong

#include <LowPower.h>
#include <avr/sleep.h>

const byte transistor = 13;
const byte interrupt_pin = 2;
volatile byte state = LOW;

uint32_t time_now;
uint32_t event_time;

void wake(){
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (0);
}  // end of wake

void setup() {
  Serial.begin(9600);
  pinMode(transistor, OUTPUT);
  pinMode(interrupt_pin, INPUT);
  digitalWrite(2, HIGH); 

    for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  //Disable ADC - don't forget to flip back after waking up if using ADC in your application ADCSRA |= (1 << 7);
  ADCSRA &= ~(1 << 7);
  
  
  //ENABLE SLEEP - this enables the sleep mode
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
  
  //DISABLE BOD
  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 BODSE bit at the same time

  //SET CLOCK PROCESSOR
  CLKPR = 0x80;
  CLKPR = 0x01;
}

void loop() {

  ADCSRA = 0;

  
  // the interrupt must be attached each loop
  attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine, RISING);
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
  detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt
  
  if (state==HIGH){
    digitalWrite(transistor,HIGH);
    delay (1000);
  }

    if (state==HIGH){
    state = LOW;
    digitalWrite(transistor,LOW);    
    delay(5000);
    }

    while (state == LOW)
{
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
}
state = LOW;

}

void interrupt_routine(){
  time_now = millis();
  if (time_now - event_time >= 10 * 1000)
  {
     event_time = time_now;
     state = HIGH;
  }
}