attachInterrupt & Sleep()

Hello all,

I'm trying to understand the interrupt logic using a bare ATMEGA328P processor. I have made a mini Arduino UNO with just that processor, an oscillator (16MHz) and a couple of condensators.
In addition, I use a LED as indicator for detection of movement, and a PIR sensor to initiate an interrupt.
Because the processor is consuming about 24mA, the battery that is powering the thing is flat after about 1 day... So, my idea was to put the processor in sleep mode in order to save power consumption.

I use the Arduino programming environment (version 1.8.5) with the code below.
At first, it seem to work. The LED goes on when I make a movement, and goes out after 10 seconds. I can repeat this several times, and observing the power consumption, it goes down from 24mA (in wake mode) to less than 1mA in sleep mode.
But, when I make several moves during the 10 seconds that the LED is on, it goes in a state that the consumption stays forever around 10mA. I think it has to do with the fact that interrupts are piled up but don't know how to deal with this situation. I also suspect I don't use the attachInterrupt and Sleep functions correctly...
I have read and tried to understand from the mass of documentation available on this subject, but I'm afraid I'm not smart enough to interpret this correctly. Can someone take a look at the code and help me out?
Thank you in advance and appologies if this is a stupid question (or cry for help...).

Danny

*/
#include <avr/sleep.h>
#include <avr/interrupt.h>

int ledPin= 13;

// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin (9600);
pinMode(ledPin, OUTPUT);
pinMode(WakeUpPin, INPUT); // Start with the LED OFF
}
// the ISR (Interrupot Service Routine) runs each time the sensor is waking up the processor

void WakeUp(){
Serial.println("WakeUp Routine");
detachInterrupt(0);
digitalWrite(ledPin, HIGH);
}

// the loop function runs over and over again forever
void loop() {
Serial.println("Main Loop");
digitalWrite(ledPin, HIGH);
delay(3000);
digitalWrite(ledPin, LOW);
attachInterrupt(0, WakeUp, RISING);
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
Serial.println("Back from Sleep");
delay(10000);
digitalWrite(ledPin, LOW);
delay(3000);
}

Please use code tags when posting code

Do not use Serial.print in an ISR. In your case you can just leave the ISR empty. Its only purpose is to wake the processor. Everything else can be done in the main code.

Thank you for you for your reply...
I removed all the Serial.Print's (they where there for debugging reasons)
I emptied the WakeUp routine
But the result is the same...
Also, I can put whatever WakeUpPin I want: 2, 4 or even 18, it results in the same behaviour.
I really would appreci


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

int ledPin= 13;
int WakeUpPin = 2;

void setup() {
pinMode(ledPin, OUTPUT);
pinMode(WakeUpPin, INPUT); // Start with the LED OFF
}
// the ISR (Interrupot Service Routine) runs each time the sensor is waking up the processor

void WakeUp(){

}

void loop() {
attachInterrupt(0, WakeUp, RISING);
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
detachInterrupt(0);
digitalWrite(ledPin, HIGH);
delay(10000);
digitalWrite(ledPin, LOW);
}


sorry for the interruption (....)
I continue:
I really would appreciate an example of code using the avr/interrupt and avr/sleep libraries. Also, the documentation refers to the datasheets in order to know which sleep modes are supported (ATMEGA328P in my case), I did that but only the sleep_mode_pwr_down seems to work.
Is there anywhere a basic explanation of the interrupt mechanism available? What I found seems to be explanations for already high level engineers, not amateurs as me... I do not understand the difference between a hardware interrupt (INT0 and INT1 ?) and the PCINTxx
If there is a forum for amateurs like me, please refer to that.

thank you in advance

Danny

Your usage of the code tags was not successful :frowning: Use the tags once and paste your code INSIDE the pair of "triangles" (I don't know the correct word)

Your description of the sketch behaviour does not make sense: All the ten seconds are spent with a delay. There is no way the sketch could possibly notice any external signal during this time.

BTW read this first. It is a good introduction to power saving. There may be a problem with the "RISING" argument. It does not work in all sleep modes and on all pins.

So External Interrupts are limited to pins 2 (Ext Int 0) or 3 (Ext Int 1). They have an interrupt dedicated to each one, are higher in priority, and have more options that the Pin Change Interrupts. Also, AttachInterrupt() only works for External Interrupts, so if you use Pin Change interrupts, you have to use a different mechanism. Which may not be that big a deal. One of the questions I have is why you are bothering with the attachInterrupt()/detachInterrupt() at all?

Pin Change interrupts have banks of pins you can use. You can choose which pins in each bank will cause an interrupt, but you can only have one interrupt service routine and one interrupt pending bit per bank.

I have a similar project that spends much of its time in low power mode, and I want to wake it up with a rising or falling edge on either one of two pins. I use pin 8 and pin 9 and use the empty interrupt just to wake up. Here's the code I used:

#include <avr/sleep.h>

const byte hallSensorPin = 9;   // PB1 = PCINT1
const byte pushButtonPin = 8;   // PB0 = PCINT0
...

void setup()
{
...
  bitSet(PCMSK0, 0);    // pin 8 = PCINT0
  bitSet(PCMSK0, 1);    // pin 9 = PCINT1
   
  MCUCR = 0x84;              // BODS | BODSE
  MCUCR = 0x80;              // Disable BOD
  ADCSRA = 0;                // Disable the ADC
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
}

void loop()
{
...
  else if (deltaTime > 2000000L)      // Has it been 2 seconds?
  {
    bitSet(PCIFR, PCIF0);     // clear any outstanding interrupts
    bitSet(PCICR, PCIE0);     // enable pin change interrupts
    sleep_enable();
    sleep_cpu();
    bitClear(PCICR, PCIE0);   // Disable the interrupt
  }
}

EMPTY_INTERRUPT(PCINT0_vect)   // This just wakes us up.  Nothing else.

Sorry for the delay, I used the time to try understand the sleep and interrupt mechanism.
I know you suggest to use the PCINT instead of the external(...) interrupts, but I believe that the use of the external interrupts is easier at this stage. The next version will be to use the PCINT's...

The result is the code below, which is not working as I expected...
I have a pure ATMEGA328P on a breadboard (however with bootloader on it), and can download without problems my code.
I connected two led's as indicators for debugging purposes.
On digital Pin 2, I connected the output of a PIR (VMA312) to generate the interrupt INT0.
After download, the LedPin goes HIGH for tree seconds as expected.
The processor goes into sleep (checked the power consumption with multimeter).
The WakeUp_Routine function attached is empty.
When making a movement before the PIR, the After_Wakeup_Now() function sets the AwakedPin HIGH as expected.
But then, the problem show up: the AwakedPin stays HIGH and the processor stay's in wakeup (checked the power consumption with the multimeter). It goes not into sleep mode again as I would expect...
I suppose it is a program logic error, but I do not see where. The main loop is pretty simple:
-Put on a LED
-Go to sleep
-Wake up and wait 10 seconds
-Go to sleep again...

any idea's?

thanks for you patience,

Danny

//*

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>


const byte interruptPin = 2;                          // Pin 2 (pin 4 on Danny'UNO)is connected to the trigger device, in this case a motion sensor
const byte ledPin= 12;                                // LED cvonnected to pin 6 is used as an indicator for sleep (when out) or awake (when powered)
int AwakedPin = 13;

void setup() {
// put your setup code here, to run once:

pinMode (ledPin, OUTPUT);
pinMode (AwakedPin, OUTPUT);
pinMode (interruptPin, INPUT_PULLUP);

}

void loop() {
   digitalWrite  (ledPin,HIGH);                      // Processor is Awake
   digitalWrite  (AwakedPin,LOW);
   delay(3000); 
     sleepNow();                                     // Call the sleep routine: sleepNow()    
     After_Wakeup_Now();                            // do something after wakeup
   delay(10000);
}

//Wakeup routine, triggered by the PIR (movement sensor) connected on pin 2

void Wakeup_Routine(){
}

void sleepNow ()
{
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   
  noInterrupts ();                                                      // make sure we don't get interrupted before we    sleep
  power_all_disable();                                                  // disables all modules 
     MCUCR = 0x84;                                                      // BODS | BODSE
     MCUCR = 0x80;                                                      // Disable BOD
     ADCSRA = 0;                                                        // disable ADC 
  sleep_enable ();                                                      // enables the sleep bit in the mcucr register
  attachInterrupt (digitalPinToInterrupt (2), Wakeup_Routine, RISING);  // wake up on RISING level on D2
  interrupts ();                                                        // interrupts allowed now, next instruction WILL be executed
  digitalWrite (ledPin, LOW);                                           // Clear the ledPin to indicate going to sleep
  sleep_cpu ();                                                         // here the device is put to sleep
}  // end of sleepNow

void After_Wakeup_Now(){
  sleep_disable ();                                                     // first thing after waking from sleep:
  detachInterrupt (digitalPinToInterrupt (interruptPin));               // stop RISING interrupt on D2
  Serial.println ("Back from Sleep");
  digitalWrite  (AwakedPin,HIGH);                                       // Indicate the processor is Awake again   
} // end of wakeup_Now

I just googled "VMA312". It is not a PIR (passive infrared receiver) but a movement sensor. I could not find documentation about its workings. Please replace the sensor with a simple button (or just a manually operated wire connection) and test again.
[edit] connect the button or wire to ground an replace "RISING" by "LOW"

What is this VMA312? My googling suggests that it's just a "spring in a can" vibration sensor, not a PIR sensor. Link to it.

oops, it's a VMA314....

sorry,

danny

I finally got something to work. Issue was apparently the place where you put the cli() function...
Here's the code (connect something to pin 12, a led or a buzzer or relay...). The code below is used with a buzzer connected to pin 12 and a PIR (VMA314).
The serialprint's are used for debugging... should be left out.
At this stage, the interrupts are cached, so when you trigger during the timeout period of the PIR, it will fire after the signal goes low again.
The objective: saving power was reached, it consumed about 0,128mA during sleep against 18mA during active state. Is better possible?

danny

//* this program will put the ATMEGA in a sleep mode and should !! wake it up via a hardware Interrupt, INT0
//*

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>


const byte interruptPin = 2;                          // Pin 2 (pin 4 on Danny'UNO)is connected to the trigger device, in this case a motion sensor
const byte ledPin= 12;                                // buzzer, led, relay...
int interrupt_flag = 0;
long counter = 0;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode (ledPin, OUTPUT);
pinMode (interruptPin, INPUT_PULLUP);
}

void loop() {
     digitalWrite(ledPin, LOW);
     sleepNow();                                     // Call the sleep routine: sleepNow()    
     Serial.print(" Waked Up!!!  flag is: ");
     Serial.print(interrupt_flag);
     counter=counter+1;
     Serial.print("---------Counter is: "); 
     Serial.println(counter);
     Alarm();
}

//Wakeup routine, triggered by the PIR (movement sensor) connected on pin 2
void Wakeup_Routine()
{
  sleep_disable();
  detachInterrupt(0);
  interrupt_flag = 1;
}

void sleepNow ()
{
  cli();                                                                       //disable interrupts
  sleep_enable ();                                                      // enables the sleep bit in the mcucr register
  attachInterrupt (0, Wakeup_Routine, RISING);          // wake up on RISING level on D2
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  ADCSRA = 0;                                                           //disable the ADC
  sleep_bod_disable();                                                //save power                                              
  sei();                                                                      //enable interrupts
  sleep_cpu ();                                                           // here the device is put to sleep
}  // end of sleepNow
 
void Alarm()
{
  //digitalWrite(ledPin, HIGH);
  tone(ledPin, 500, 1000);
  delay(1000);
  tone(ledPin, 200, 1000);
  delay(1500);
  //digitalWrite(ledPin, LOW);
  interrupt_flag = 0;
}

You should get it below 1µA in sleep plus the current drawn by the sensor

less than 0,001 mA???
What do I need to do more then? BOD is off, ADC is off and power_down mode.
Is there more to do?

danny