Arduino Sleep after idle time and then wake on IR Remote button push

I have created an automated window blind controller using an arduino pro mini, servo, and IR receiver and remote. I have programmed the MCU to move the servo up and down based on specific buttons on the IR remote. All works just as intended with some additional tweeking for a crappy knock-off Futaba S3003 servo. But I digress.

I'm trying to find a way to put the arduino into sleep mode (not sure which mode is the best for this application) if 30 seconds has elapsed without receiving a signal from the IR remote. Then when a signal is received, it wakes up moves the servo to the designated position and starts the sleepCounter all over.

For the sake of brevity, here is a representation of my loop along with where I think the sleep code should go. I am new to arduino and somewhat familiar with C++. I've got a couple of motion sensor light projects under my belt along with some LDR night light projects so I feel like I have a solid grasp of the basics but I can't figure out how to implement a sleep function. I've done quite a bit of research and tested the tutorials on sleep mode but I can't figure out how to implement with an IR Remote. What I want to do seems simple enough I just don't know where to begin. I would much rather have hints rather than answers so that I gain a better understanding of the sleep mode capability.

I am more than happy to provide the complete sketch if needed or wanted.

Thanks, Sean

void loop()
{
  if sleepCounter >= 30000
  {
    sleepCounter = 0;
    gotoSleep;
  }
  else if results.value == DOWN_ARROW
  {
    move servo down;
    sleepCounter = 0;
  }
   else if results.value == UP_ARROW
  {
     move servo up;
     sleepCounter = 0;
  }
   else
  {
    counter++;
  }
}

Then when a signal is received, it wakes up moves the servo to the designated position and starts the sleepCounter all over.

The only thing that can wake a sleeping Arduino is an external interrupt. An IR-based remote control does NOT generate external interrupts. With the Arduino sleeping, you can't receive IR signals.

An automated window blind controller implies a house with windows with blinds. The house has power, doesn't it?

What is the purpose of sleeping?

Paul thanks for your feedback. My answers and additional questions below:

An automated window blind controller implies a house with windows with blinds. The house has power, doesn't it? What is the purpose of sleeping?

I am looking to put this project on ALL of my windows (say 15 give or take a few). I did not want to run power cords off every window to the closest outlet. So, I am planning on running the devices off some reclaimed rechargeable LiPo batteries. I was hoping to keep the board asleep most of the time and just wake up when I want to remotely control the position of the blinds. I am also considering a LDR sensor as the input. In either case I see the MCU doing something no more than 3 times a day (sun-up, mid-day, sun-down). I see no need for it to be a full power the remainder of the time just sitting idle running through a loop that is going to happen for a while.

The only thing that can wake a sleeping Arduino is an external interrupt. An IR-based remote control does NOT generate external interrupts. With the Arduino sleeping, you can't receive IR signals.

Ok, how bout a toggle or momentary switch? If not then I don't understand what an external interrupt is and how I do it. I figured the IR remote with an IR receiver connected to one of the arduino pins would serve as an interrupt. When I push the button on the remote the IR receiver connected to the appropriate interrupt pin triggers a wake up call. Call me clueless in need of more study!

Ok, how bout a toggle or momentary switch?

If properly wired to an external interrupt pin, then it can generate an external interrupt to wake up the Arduino.

I figured the IR remote with an IR receiver connected to one of the arduino pins would serve as an interrupt.

The Arduino has to be actively exciting the IR receiver, and reading any input data. If not being actively excited, the IR receiver doesn't receive data. Without incoming data, there is nothing to wake up the Arduino.

It would be like trying to read distance data from a Ping sensor that was not being told to send a pulse.

PaulS:
If properly wired to an external interrupt pin, then it can generate an external interrupt to wake up the Arduino.

Ok, great. Thanks Paul. Off to figure it out. Appreciate the help. If all doesn't work out then in the immortal words of Arnold Schwarzenegger; "I'll Be Back!"

Sean

Alight, I'm getting closer to a working sleep plan for my automated blind control. I was able to find a basic sleep sketch that I modified to work off a counter. When the counter reaches a certain value, the arduino goes to sleep. I can then wake the arduino with an external interrupt (momentary button) and the counter starts all over again. Ultimately, I will replace the momentary switch with a PIR sensor that wakes the arduino up if someone comes into the room and then puts it back to sleep after a period of detecting no motion. Below is the simple code that works as intended.

#include <avr/sleep.h>

// hardware interrupt
#define INT1 1
#define INT1_PIN 3
 
#define BLINK_PIN 13
 
int buttonPushed;
int sleepCounter = 0;
const int sleepDelay = 15;
 
void doWhileAwake()
{
  digitalWrite(BLINK_PIN, HIGH);
  delay(1000);
  digitalWrite(BLINK_PIN, LOW);
  delay(1000);         
}
 
void prepareToSleep()
{
  digitalWrite(BLINK_PIN, LOW);
}
 
void wake()
{ 
  buttonPushed=0;
}
 
void toggle()
{
  detachInterrupt(1);
  if (buttonPushed ==0)
  { 
    buttonPushed=1;
  }
}
 
void sleep()
{
  prepareToSleep();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  attachInterrupt(INT1,wake,LOW);
  sleep_mode(); // sleeping
}
 
void setup()
{
  pinMode(INT1_PIN, INPUT);
  attachInterrupt(INT1,toggle,LOW);
  buttonPushed=1; // set to 1 = sleep after reset. 0 = run loop after reset
  pinMode(BLINK_PIN, OUTPUT);
}
 
void loop()
{
  if (sleepCounter >= sleepDelay)
  {
    sleepCounter = 0;
    sleep();
  }
  else
  {
  attachInterrupt(INT1,toggle,LOW);
  }
  sleepCounter++;
  doWhileAwake();
}

However, when I incorporate the above code into my automated blind controller sketch, the arduino immediately goes to sleep. Does anyone see something that might be causing it to skip directly to sleep mode?

#include <VarSpeedServo.h>
#include <IRremote.h>
#include <avr/sleep.h>

//CONFIGURATION
const int BLINK_PIN = 13;
const int PIN_LED = 10;
const int PIN_IR = 11;          //Input for Seagate Media Player Remote
const int servoPin = 9;         //Input pin for servo
IRrecv irrecv(PIN_IR);        
decode_results results;

//hardware interrupt pins
#define INT0 0
#define INT0_PIN 2
#define INT1 1
#define INT1_PIN 3

//CONVERT IR REMOTE VALUES TO COMMON BUTTON TERMS
#define POWER       0x61471CE3
#define HOME        0x61479C63
#define UP_ARROW    0x614702FD
#define DOWN_ARROW  0x6147827D
#define LEFT_ARROW  0x6147E01F
#define RIGHT_ARROW 0x6147609F
#define OK          0x6147708F
#define BACK        0x6147D827
#define MENU        0x614758A7
#define INFO        0x6147E21D
#define REWIND      0x61474AB5
#define PLAY/PAUSE  0x6147F20D
#define FFWD        0x61478A75
#define STOP        0x61472AD5
#define PREVIOUS    0x61478C73
#define NEXT        0x61470CF3
#define MUTE        0x61479A65
#define UP_ZOOM     0x6147AC53
#define DOWN_ZOOM   0x614710EF
#define UP_VOLUME   0x61474CB3
#define DOWN_VOLUME 0x6147CC33

//VARIABLE FOR THE FUTABA S3003 CLONE SERVO ANGLE & SPEED OF ROTATION
int initial_ms = 2350;          //On start millis angle for the servo
const int max_ms = 2350;        //Millis angle for "Fully Closed - Down"
const int upA_ms = 1900;        //Millis angle for "Partially Closed - Down"
const int mod_upA_ms = 1850;    //Modified millis angle for clockwise rotation to "Partially Closed - Down"
const int mid_ms = 1468;        //Millis angle for "Fully Open"
const int mod_mid_ms = 1420;    //Modified millis angle for clockwsie rotation to "Fully Open"
const int downA_ms = 1050;      //Millis angle for "Partially Closed - Up"
const int mod_downA_ms = 975;   //Modified millis angle for clockwise rotation to "Partially Closed - Down"
const int min_ms = 600;         //Variable for "Fully Closed - Up"
const int sleepDelay = 15;
int dest = 0;                   //Variable for Servo destination depending on IR Remote button pushed
const int spd = 20;             //Variable to control the speed of the servo moving between positions; 5 is slow
VarSpeedServo myservo;          //Create servo object to control a servo 
int pos = 0;                    //Keep track of servo angle so we don't send signal to the servo without reason
int prevPos = 0;                //Keeps history of prior state
int buttonPushed;
int sleepCounter = 0;

void doWhileAwake()
{
  if (irrecv.decode(&results))
  { 
    irrecv.resume();
    if (results.value == DOWN_VOLUME)
    {
      sleepCounter = 0;
      if (initial_ms == min_ms)
      {
        dest=mod_downA_ms;
        initial_ms=downA_ms;
        pos=1;
      }
      else if (initial_ms == downA_ms)
      {
        dest=mod_mid_ms;
        initial_ms=mid_ms;
        pos=2;
      }
      else if (initial_ms == mid_ms)
      {
        dest=mod_upA_ms;
        initial_ms=upA_ms;
        pos=3;
      }
      else if (initial_ms == upA_ms)
      {
        dest=max_ms;
        initial_ms=max_ms;
        pos=4;
      }
    }
    if (results.value == UP_VOLUME)
    {
      sleepCounter = 0;
      if (initial_ms == max_ms)
      {
        dest=upA_ms;
        initial_ms=upA_ms;
        pos=5;
      }
      else if (initial_ms == upA_ms)
      {
        dest=mid_ms;
        initial_ms=mid_ms;
        pos=6;
      }
      else if (initial_ms == mid_ms)
      {
        dest=downA_ms;
        initial_ms=downA_ms;
        pos=7;
      }
      else if (initial_ms == downA_ms)
      {
        dest=min_ms;
        initial_ms=min_ms;
        pos=8;
      }
    }
      
    //IF the IR remote command is different from last sample then execute servo controls
    if (pos != prevPos)
      { 
        myservo.attach(servoPin);     //Connect to servo
        //Move servo to new position
        //digitalWrite(ledColor, HIGH);
        myservo.write(dest, spd, true);
        //digitalWrite(ledColor, LOW);
        myservo.detach();                 //Detach servo to keep it from humming on strain
      }     
      prevPos = pos; //Remember state so we can compare it again next round
  }
}

void prepareToSleep()
{
  // When the sleepCounter reaches the delay time 
  // do some prep before sleeping to get in the
  // desired state
  // digitalWrite(BLINK_PIN, LOW);
}

void wake()
{
  // This is the callback that runs when the
  // chip is waking up.
  buttonPushed=0;
 
  // Can set variables and check stuff.  Cannot do fancy stuff.
  // Program flow returns to line after 'sleep_mode()' in sleep.
  // No code is required here.
}

void toggle()
{
  // set a flag telling system to sleep on next loop iteration.
  // this is the normal running state callback triggered by the
  // switch.
  detachInterrupt(1);    // this seems to be allowed in callback
  if (buttonPushed ==0)  // just in case, ignore switch if already
  { 
    buttonPushed=1;      // doing it
  }
}

void sleep()
{
  prepareToSleep();  //application specific
 
  // YOU ARE GETTING SLEEPY.....
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  attachInterrupt(INT1,wake,LOW); // rebind interrupt
  sleep_mode(); // sleeping
 
  // program returns here, after 'wake' callback
  // now back to loop
}

void setup()
{
    Serial.begin(9600);
    pinMode(INT1_PIN, INPUT);  // Set switch pin to input
    attachInterrupt(INT1,toggle,LOW);  // To limit current
    buttonPushed=1;  // Set to 1 = sleep after reset; 0 = run loop after rest
    //pinMode(BLINK_PIN, OUTPUT);
    myservo.attach(servoPin);
    myservo.write(initial_ms, spd, true);
    //pinMode(PIN_LED, OUTPUT);
    //pinMode(PIN_IR, INPUT);
    irrecv.enableIRIn();
}

void loop()
{
  if (sleepCounter >= sleepDelay)
  {
    sleepCounter = 0;
    sleep();
  }
  else
  {
  // reset button events to sleep
  attachInterrupt(INT1,toggle,LOW);
  }
  sleepCounter++;
  //Serial.println(sleepCounter);
  // Control Blinds
  doWhileAwake();
}

Thanks, Sean

You should not be attaching and detaching interrupts.

How long do you think it takes for loop() to iterate 15 times? Not very long. The Arduino is not immediately going to sleep. But, it sure doesn't take long before it does.