Very Simple Project - Using Sleep - Need Help Debugging

Forum,

After my first project, the ardustat { http://rsangole.wordpress.com/ }, which was much more complex, I figured this new project would be easy. But I can't figure out where I'm going wrong. Please do advise.

Problem - While entering the garage through my house, garage is very dark, and the light switch isn't in convenient place.
Solution - Use magnetic door contact switch to detect door being opened --> Wake Arduino from sleep --> Use relay to switch on lights --> Wait 3 min (for me to get my stuff) --> Switch off lights via relay --> Go to sleep

Simple circuit layout: http://i.imgur.com/kTf0GWm.png
The momentary push button simulates door opening event; I keep it pushed for now. (Door contact switch is in shipping).

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

#define DOOR 4 //Contact switch input
#define LIGHT 5 //Relay control output

void setup(void){
  pinMode(LIGHT,OUTPUT);
  digitalWrite(LIGHT,LOW);
}

void loop(void){
  digitalWrite(LIGHT,LOW); //Light On
  delay(3000); //Wait for a while for me to get my stuff -- Right now 3 sec, later 3 min
  digitalWrite(LIGHT,HIGH); //Light off
  go2sleep(); //Go to low power, wait for interrupt
}

void intFun(){
  sleep_disable();
  detachInterrupt(0);
}

void go2sleep(){
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  attachInterrupt(0,intFun,LOW);
  sleep_mode();
}

It works correctly always when --> Time(button press) < time(delay). i.e.: when I keep the button pressed ("door opened" for less than 3 sec allowing the relay to open before the delay(3000) runs out, everything works fine. Sketch works repeatedly with no problems.

It doesn't work when--> Time(button press) > time(delay). In this case, sketch works fine on 1st button press event (Pin 5 goes HIGH->LOW.) I keep the button pressed, arduino goes to sleep, then I release the button. On subsequent button presses, the arduino doesn't wake up after sleep. I have to reset.

I don't know what the relation is for the state of an input variable (in this case LOW/HIGH state of pin 5) during going-to-sleep on the arduino's ability to detect the pin going from HIGH to LOW.

Advise?

Thanks,
Rahul

Very nice job on your projects and web site. I like the Google gauges, I've played with them a bit, but I'll have to have a look at your code too.

Here is sleep code I use for the 328P, I think it's very close to what you're trying to do. (BTW, you could do without the external pullup resistor, use the 328P's internal pullup.)

It gets discussed regularly, but if you're not aware, Arduino boards aren't the best for low power projects:
http://forum.arduino.cc/index.php?topic=164146.msg1232507#msg1232507

I expect it's because you're using the INT0 interrupt, which is active LOW, not the pin change interrupt. Which means, as long as you hold the button down, it will fire the INT0 interrupt. Over and over again. Well, in this case, it only happens once, because your interrupt service routine disables the interrupt.

So here's what I expect is the sequence of events:

  Init...
loop() is called by the system
  digitalWrite(LIGHT,LOW);
  delay(3000);
  digitalWrite(LIGHT,HIGH);
  go2sleep();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    attachInterrupt(0,intFun,LOW);
    sleep_mode();
      Processor powers down here and waits for you to open the door / push the button
      Button Press invokes the interrupt service routine
      sleep_disable();
      detachInterrupt(0);
      returns from InterruptServiceRoutine
    returns from go2sleep()
  returns from loop()
loop() is called again
  digitalWrite(LIGHT,LOW);
  delay(3000);
  digitalWrite(LIGHT,HIGH);
  go2sleep();
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
      sleep_enable();
      attachInterrupt(0,intFun,LOW);
      At this point, since the button is still held down, the interrupt service routine is invoked    
        sleep_disable();
        detachInterrupt(0);
        returns from InterruptServiceRoutine
      sleep_mode()
      The processor goes to sleep, just like you told it to.
      But since you just detached the interrupt, it can't wake back up.

One would think that because you called sleep_disable() in the interrupt service routine, you wouldn't go back to sleep. However, sleep_mode() calls sleep_enable(), sleep_cpu(), and sleep_disable(). Which re-enables sleep so you can go to sleep permanently.

There are several possible ways to fix the problem. An easy one would be to check to make sure the door is closed before calling go2sleep(). More elegant probably is to use the PinChange interrupt instead of just LOW.

What I do is use an empty interrupt service routine. I don't really need it to do anything, just wake up the processor.

Another possibility is to simply call sleep_cpu() instead of sleep_mode(). I think that would prevent it from actually sleeping while the door is open. Maybe not exactly what you were intending with this particular code, but probably what you really want it to do, after all. The light would then stay on until at most 3 seconds after you shut the door.