interrupt & sleep mode question re callback method

Hi,

Re the code below, I'm finding that if I continually press the button I have setup to trigger the interrupt with code as it is the program kind of locks up after it goes into sleep.

BUT if I move the "detachInterrupt(0);" statement out of the call back function, and just put it at the bottom of the enterSleep() method than things seems to work robustly.

Anyone have any ideas why this is?

#include <avr/sleep.h>

int wakePin = 2;                 // pin used for waking up
int ledPin =  13;
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter

void setup()
{
  pinMode(wakePin, INPUT);
  pinMode(ledPin, OUTPUT);  
  Serial.begin(9600);
}

void wakeUpNow()        // here the interrupt is handled after wakeup
{
   detachInterrupt(0);      // disables interrupt 0 on pin 2 so the 
}


void sleepNow()         // here we put the arduino to sleep
{
    Serial.println("sleepNow");
    attachInterrupt(0,wakeUpNow, RISING); // use interrupt 0 (pin 2) and run function
    delay(100);     
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();          // enables the sleep bit in the mcucr register
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

    sleep_disable();         // first thing after waking from sleep:
}

void loop()
{
  // display information about the counter
  digitalWrite(ledPin, HIGH);
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second
  digitalWrite(ledPin, LOW);

  // check if it should go to sleep because of time
  if (count >= 5) {
      
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep 
                      //function will provoke a Serial error otherwise!!
      
      count = 0;
      sleepNow();     // sleep function called here
  }
}

These two are mutually exclusive...

attachInterrupt(0,wakeUpNow, RISING); // use interrupt 0 (pin 2) and run function

set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here

A RISING interrupt requires the primary clock to run. SLEEP_MODE_PWR_DOWN shuts off the primary clock.

umm, but the code works fine for me - the only issue I had was (being pedantic) if I press the button on and off really quickly you can end up with the last message not reflecting the final state of the button. I assume this is due to perhaps the final button state change occurring in the short interval before the interrupt was enabled again...

So my question was if I just wanted to avoid this how to best do this?

This is the description you gave...

I'm finding that if I continually press the button I have setup to trigger the interrupt with code as it is the program kind of locks up after it goes into sleep

Are you saying that I researched a problem that you are not in fact trying to fix?

this is the problem I'm trying to fix - I just wanted to make sure I made it clear the code is working fine except the use case when I toggle the button on/off very fast (where in this situation the issue appears)

When using external interrupts (pin 2 or pin 3) to wake up from sleep, the following conditions must be met (e.g. for pin 2):

  1. External interrupt for pin 2 must be enabled (type LOW).
  2. A low level on pin 2 will trigger wake-up

To use this, you should enable pullup on pin 2 and then put your Arduino to sleep. Once it sees a low level on pin 2, it will wake up and give you a chance to act on the event.

I would put the call to attachInterrupt in the setup function and leave it at that (no detach is needed). The handler could be an empty function.

Coding Badly wrote:

These two are mutually exclusive...
attachInterrupt(0,wakeUpNow, RISING); // use interrupt 0 (pin 2) and run function
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here

I'm not absolutely sure, but I think the two are independant.

That is, wake-up will trigger on a low level irrespective of interrupt type. The interrupt handler however will only be called if the pin transition match the selected interrupt type. This can lead to a scenario where you wake-up and go back to sleep without actually servicing the interrupt. The interrupt flag will then remain set and no further wake-ups will occur.

Re "I would put the call to attachInterrupt in the setup function and leave it at that (no detach is needed). The handler could be an empty function."

Thanks, but how would this solve the issue I have? Don't quite follow.

EDIT: I see you referred to leaving the interrupt in place the whole time. I'll try but i think i had other side effects when I was trying it this way

@BenF:
This appears to be the most relevant passage from the datasheet...

When the INT0 or INT1 interrupts are enabled and are configured as level triggered, the interrupts will trigger as long as the pin is held low. Note that recognition of falling or rising edge interrupts on INT0 or INT1 requires the presence of an I/O clock, described in “Clock Systems and their Distribution” on page 26. Low level interrupt on INT0 and INT1 is detected asynchronously. This implies that this interrupt can be used for waking the part also from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode.

I'm not absolutely sure, but I think the two are independant.

They're interlinked by the I/O clock. After the clock is stopped by power-down, the rising edge can't be detected.

That is, wake-up will trigger on a low level irrespective of interrupt type

My interpretation is that a level interrupt is only generated if explicitly configured. Implying that INT0 configured for a rising edge will not trigger an interrupt on a low level.

The issue you have is most likely with using the "RISING" interrupt type. Change it to type LOW (trigger it accordingly) and you should be ok.

The call to detachInterrupt serves no purpose in your code, so you may as well take it out.

Coding Badly:

First, I think your first post is spot on. Use type LOW to enable wake-up, problem solved.

Then trying to understand what will happen if you don't is a different matter:

My interpretation is that a level interrupt is only generated if explicitly configured. Implying that INT0 configured for a rising edge will not trigger an interrupt on a low level.

I agree with above, however (I think) wake-up is independent. That is as long as some type of interrupt is enabled (RISING, FALLING, LOW, CHANGE), it will wake up when detecting a low level. The handler however will not be called unless the pin transition match the interrupt selected.

The datasheet is clear on this behaviour when it comes to pin-change interrupts, but less so for external interrupts. As such it is a speculation on my part.

thanks guys - an update

a) re triggering using Mode = LOW, the only issue for me here is that I'm using a Reed Switch to send door open/close. So I want to wake the arduino up in both the cases when the door has just been open, or just been closed. So I'm just using the reed switch to help set the interrupt pin to either high or low and using this. (in my latest tests I was actually using mode = CHANGE, as opposed to RISING too, but still with the same issue). So I'm not sure how I'd use the trigger on LOW (i.e. would miss out on the "door open" event)

b) re taking out the detachInterrupt - I took this out but this didn't help - I can still simulate the issue - in fact I'm not sure what happens in this case - what happens if there's another interrupt come whilst you are still processing the previous interrupt? (i.e. the unit hasn't been put back into sleep yet) - does it get ignored or buffered?

c) re putting in the attachInterrupt in setup() - tried this but this didn't help - I can still simulate the issue

So for the moment the 2 things that come to mind for the issue are:

i) the period where the interrupt wasn't enabled (so there was a blind spot) - but testing with the detachInterrupt taken seems to have shown this isn't the issue (but assuming the interrupts buffer here perhaps?)

ii) the other point I note is that in the code is that the actual status of the door is read several lines after the interrupt occurs - so it's possible that the door status changed between the interrupt being received and then the code reading the state - but than if the status was read later, then you would think it would be more likely to be correct if it was the last interrupt in a string of them in succession no... so this doesn't explain it either

So overall I'm still not really sure how the incorrect state occurs :-?

If there is a true blind spot picking up state changes (from idea i) above) then that might imply I can never really fix it. So in this case would the only work around be to have a separate watchdog timer come in on the 2nd interrupt, so every so often a double check on the state is performed perhaps? Don't really want to have to go to the trouble of implementing this for this edge case, but it would be interesting to know if this was the only real way to make the design really robust.

a) re triggering using Mode = LOW, the only issue for me here is that I'm using a Reed Switch to send door open/close. So I want to wake the arduino up in both the cases when the door has just been open, or just been closed. So I'm just using the reed switch to help set the interrupt pin to either high or low and using this. (in my latest tests I was actually using mode = CHANGE, as opposed to RISING too, but still with the same issue). So I'm not sure how I'd use the trigger on LOW (i.e. would miss out on the "door open" event)

A "pin change" interrupt is a better choice in this situation.

The good news is: A pin change interrupt will give you what you want (an interrupt from low-to-high and an interrupt from high-to-low). Any pin can be configured to generate a pin change interrupt. A pin change interrupt will wake the processor.

The bad news: The Arduino run-time-library does not have support for pin change interrupts. However, there are many examples and even a library in the forum.

A pin change interrupt will give you what you want (an interrupt from low-to-high and an interrupt from high-to-low).

Thanks. Do you understand specifically what the issue I'm having and how this will fix it? The penny hasn't dropped for me yet still :frowning:

Thanks.

You are welcome.

Do you understand specifically what the issue I'm having and how this will fix it?

My understanding is that there are two issues. The first is getting the processor to wake when there is activity on the reed switch input. Using a pin change interrupt should solve this problem.

I believe the second problem is a "race condition". I cannot say for certain if the code you posted here is suffering from the second problem but this code very likely is...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1277510728

Let's say the reed switch input goes high waking the processor then generating an interrupt. The code in updateBase prints out the current state of the reed switch. Before the processor is put back to sleep, the reed switch input goes low. The interrupt will also be generated before the processor is put to sleep. Once the processor is put to sleep, the only thing that will wake it is if the reed switch input goes high. Until that happens, the output from the Sketch doesn't match the state of the reed switch.

My suggestion is to get pin change interrupts working and then revisit the other problem.

Thanks again

Re the 2nd problem suggestion, thanks, I'll look into this. My feeling is you're probably right about whats going on here.

Re the 1st problem you refer to, I don't really see this as a problem as the interrupt technique works, but just suffers from a probable race condition I think. Make any sense?

Testing with conflicting or irrelevant configurations is not likley to get you anywhere. Get the basics right first and then work on issues (if any remain).

Why not take a step back and create a small schematic showing how you want to wire the reed sensor (and/or the button you use for testing). Then you need a sketch to match the schematic and your requirements.

If your requirement is to detect and wake up from both high to low and low to high transitions, you need to abandon external interrupts (attach/detach). Read up on pin change interrupts and implement this in your sketch. Leave the interrupt service routine empty.

Contact bounce is likely to be an issue both for the button you test with and the reed switch. Once you have the basics right however, this can be sorted out.

thanks Ben - sounds like good advice - I'll google & read up on "pin change interrupts" - was there a particular good example or reference you have in mind (just in you have one you recommend)

Hi guys,

Starting to look at the docs one concern I have is that it seems like the ONLY sleep mode available for using "Pin Change" is IDLE (i.e. can't use Power-save, Standby etc modes). Is this your interpretation of Table 9-1 of ATmega168 data sheet?

The issue here is that based on the current measurements I've been taking on my Arduino Pro in various sleeps modes, that the life time of a 9V battery to power the door open/closed detector would only be able 1 DAY using IDLE mode (i.e. it would jump to 20 days if I can sleep to POWERDOWN mode).

Table (I measured this using the 9V battery when the unit was in each of the modes)

thanks

PS. I'd really want my remote battery based door detector units to last 6 months, but based on the readings above it doesn't seem you could ever do this with an Arduino anyway (i.e. still chews 1mA even when in powerdown mode). Might raise a separate question re this rather than diverting focus on this thread.

Is this your interpretation of Table 9-1 of ATmega168 data sheet?

It is neither my interpretation nor my experience.

Level interrupt for INT1 and INT0, pin change interrupt, TWI address match interrupt, and watch-dog timer interrupt wake the processor from all sleep modes.