LowPower: External wakeup in example

Arduino Uno & Nano...
I try to do my research before coming to this forum. Frequently, the library examples provided on GitHUb contain the answers. However in this case, I've looked all over the web and was not able to find the answer. Through the Arduino IDE Library Manager "More info" link I ended up HERE. This is the example provided with a few of my additions identified with the "//Added" comment.

My question is, how does this code demonstrate the wakeup ability?

// **** INCLUDES *****
#include "LowPower.h"

// Use pin 2 as wake up pin
const int wakeUpPin = 2;
const int ledPin = LED_BUILTIN;  //Added

void wakeUp() {
  digitalWrite(ledPin, HIGH);  //Added
}

void setup() {
  // Configure wake up pin as input.
  // This will consumes few uA of current.
  pinMode(wakeUpPin, INPUT);
  pinMode(ledPin, OUTPUT);  //Added
}

void loop() {
  // Allow wake up pin to trigger interrupt on low.
  // attachInterrupt(ledPin, wakeUp, CHANGE);  //Added
  attachInterrupt(0, wakeUp, LOW);

  // Enter power down state with ADC and BOD module disabled.
  // Wake up when wake up pin is low.
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

  // Disable external pin interrupt on wake up pin.
  detachInterrupt(0);

  // Do something here
  // Example: Read sensor, data logging, data transmission.
  digitalWrite(ledPin, HIGH);  //Added
}

Did you try the posted sketch to see if it works?

(You need to connect an external 4.7k pull-up resistor with DPin-2 and then bring down DPin-2 to GND using a push button to wake-up the MCU from sleep.)

Insert delay(1000); after detachInterrupt(0); code in the loop() function and then execute digitalWrite(ledPin, LOW); and NOT digitalWrite(ledPin, HIGH);.

Yes. I tried the posted sketch and had no idea what else needed to be done to see some results. The example provides no instruction. In your reply, I gather that I should connect a button to GND and pin 2 to see something happen. The attempt to power down the sketch is part of a much larger sketch that uses batteries to power a 5v LED. I want to have it on for several hours and to power down until the next day. I'm using an old TV remote to power down the Arduino but can't get it to wake up. This is the remote code:

  /////////// IR Receiver  /////////////
  if (IrReceiver.decode()) {
    uint16_t address = IrReceiver.decodedIRData.command;
    if (address == 4) {
      Serial.println(address);
      digitalWrite(12, LOW);
      KillLED(0);
    } else if (address == 5) {
      wakeUp();
      Serial.println(address);
      digitalWrite(12, HIGH);
    }
  }
  IrReceiver.resume();

Pin 12 is the 5v LED pin. When "(address == 4)" from the remote control, it calls the killLED function which is this:

  attachInterrupt(digitalPinToInterrupt(2), wakeUp, LOW);  
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);     

That kinda works. What do I need to do to use the IRReceiver rather than a button to ? My goal is to toggle the power on and off.

Thank you for taking the time to answer.

What are you using? An Arduino UNO?

Yes. Arduino Uno.

Try that and see what happens. It is always a good idea to test project ideas in small pieces, then starting putting them together.

Please, post the complete sketch.

Which button of the TV Remote Controller corresponds to address 4 and which one corresponds to address 5?

Button 1 on the remote generates 4 in the sketch. Button 2 generates 5.

I'm reluctant to post the complete sketch because it is a work-in-progress but here goes...

/*
February, 2024
This sketch will identify the position of the switch.
The switch position will identifywhether that a 2, 3 and 4 cell 
LIPO battery is connected. When the battery is depleted to 3.2 volts
per cell, it will turn off the LED and power down.

NOTE: Some Nano's require Tools>>Processor ATmega328P (Old Bootloader)
*/

#include <LowPower.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "IRremote.hpp"

#define IR_RECEIVE_PIN 9     //IR Receiver
#define SCREEN_WIDTH 128     // OLED display width,  in pixels
#define SCREEN_HEIGHT 32     // OLED display height, in pixels
#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);  //Declare oled object

const int switchOne = 3;  // digital pin Position 1
const int switchTwo = 4;  // digital pin Position 3
// const int ledPin = LED_BUILTIN;
const int ledPin = 12;  //Pin connected to the LED that identifies the switch position
const int wakeUpPin = 2;

int oneState = 0;  //Initialize current state
int threeState = 0;
int pos1Loops = 0;  //Initialize the loop count for each position
int pos2Loops = 0;
int pos3Loops = 0;

const int voltageSensor = A0;       //Pin connected to voltage regulator
float vOUT = 0.0;                   //
float vIN = 0.0;                    //
float R1 = 30000.0;                 //Voltage Regulator resistor 1
float R2 = 7500.0;                  //Voltage Regulator resistor 2
int value = 0;                      //Value read from voltage pin
const float voltage_divider = 3.9;  //Adjusted. Not 5.0 as in examples
String position_identifier = "Initialized";

//Cut off voltage depending on battery size
const float pos1CutoffVoltage = 6.4;   //2 cell battery
const float pos2CutoffVoltage = 9.6;   //3 cell battery
const float pos3CutoffVoltage = 12.8;  //4 cell battery

bool ledIsOn = false;

void wakeUp() {
  // delay(5000);
  digitalWrite(12, HIGH);
  Serial.println("In the wakeUp function");
  // delay(1000);
}

void setup() {
  Serial.begin(9600);                //Print output
  pinMode(switchOne, INPUT_PULLUP);  //switchOne digital mode
  pinMode(switchTwo, INPUT_PULLUP);  //switchTwo digital mode
  pinMode(ledPin, OUTPUT);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  //IR Receiver
  pinMode(wakeUpPin, INPUT_PULLUP);
}

void loop() {
  // delay(5000);
  // // attachInterrupt(2, wakeUp, LOW);
  // attachInterrupt(digitalPinToInterrupt(2), wakeUp, CHANGE);
  // // LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  // attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);  //LOW, CHANGE, RISING, FALLING
  // LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);     //Power down
  // detachInterrupt(wakeUpPin);
  // delay(1000);

  oneState = digitalRead(switchOne);
  threeState = digitalRead(switchTwo);

  value = analogRead(voltageSensor);
  //vOUT = (value * 5.0) / 1024.0;
  vOUT = (value * voltage_divider) / 1024.0;
  vIN = vOUT / (R2 / (R1 + R2));

  //////// TESTING ////////////
  vIN = 13;  //TEST 6.3, 9.5, 12.7 //
  // Serial.print("value = ");
  // Serial.println(value);
  // Serial.print("vOUT = ");
  // Serial.println(vOUT);
  // Serial.print("vIN = ");
  // Serial.println(vIN);
  ///////////////////////////////////

  if (oneState == LOW) {
    Serial.println("Position 1");
    while (pos1Loops < 4) {
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(500);
      digitalWrite(ledPin, LOW);
      pos1Loops++;
    }
    threeState = LOW;
    if (vIN < pos1CutoffVoltage) {
      KillLED(vIN);
    } else {
      digitalWrite(ledPin, HIGH);
      position_identifier = "2 Cell Battery";
    }

  } else if (threeState == LOW) {
    Serial.println("Position 3");
    while (pos3Loops < 4) {
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(500);
      pos3Loops++;
    }
    oneState == LOW;
    if (vIN < pos3CutoffVoltage) {
      KillLED(vIN);
    } else {
      digitalWrite(ledPin, HIGH);
      position_identifier = "4 Cell Battery";
    }
  } else {
    Serial.println("Position 2");
    while (pos2Loops < 4) {
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(500);
      digitalWrite(ledPin, HIGH);
      delay(200);
      digitalWrite(ledPin, LOW);
      delay(500);
      pos2Loops++;
    }
    oneState == LOW;
    if (vIN < pos2CutoffVoltage) {
      KillLED(vIN);
    } else {
      digitalWrite(ledPin, HIGH);
      position_identifier = "3 Cell Battery";
    }
  }
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.clearDisplay();       // clear display
  oled.setTextSize(1);       // text size
  oled.setTextColor(WHITE);  // text color
  oled.setCursor(15, 0);
  oled.println(position_identifier);
  oled.setTextSize(2);
  oled.setCursor(0, 15);  // position to display
  oled.print(vIN);        // text to display
  oled.println(" Volts");
  oled.display();
  delay(1000);
  /////////// IR Receiver  /////////////
  if (IrReceiver.decode()) {
    uint16_t address = IrReceiver.decodedIRData.command;
    if (address == 4) {
      Serial.println(address);
       delay(1000);
      attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);  //LOW, CHANGE, RISING, FALLING
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);             //Power down
      detachInterrupt(wakeUpPin);
      delay(1000);
      // digitalWrite(12, LOW);
      // KillLED(0);
    }  //else if (address == 5) {
    //   wakeUp();
    //   Serial.println(address);
    //   digitalWrite(12, HIGH);
    // }
  }
  IrReceiver.resume();
}

void KillLED(float vIN) {
  Serial.print("In KillLED fuction.");  // Voltage =
  Serial.println(vIN);
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.clearDisplay();       // clear display
  oled.setTextSize(1);       // text size
  oled.setTextColor(WHITE);  // text color
  oled.setCursor(20, 0);
  oled.println("Powered Down");
  oled.setTextSize(2);
  oled.setCursor(0, 15);  // position to display
  oled.print(vIN);        // text to display
  oled.println(" Volts");
  oled.display();
  // delay(5000);
  // attachInterrupt(2, wakeUp, LOW);
  attachInterrupt(digitalPinToInterrupt(2), wakeUp, LOW);  //LOW, CHANGE, RISING, FALLING
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);     //Power down
}
attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);

When is "LOW" the TX LED on the Arduno Uno pulsates and the 5v LED on pin 12 remains constantly lighted. This is the desired behavior..

When the remote sends a "4" indicating the number one button on the remote was pressed, the TX LED no longer pulsates. It remains. But at the same time, the wakeUP() function was called printing out "In the wakeUp function" very rapidly. Pressing the number one button on the remote again has no affect.

What do I need to do to wake up the Uno?

I tried to limit the code by my original post using the LowPower library example but the whole sketch is in this post.

Thank you for your time and opinion.

Fine!

I will try to show you how to put UNO into sleep (Power-down mode) by pressing Button-1 and wake-up the UNO by pressing Button-2. I am working on it.

That would be great because I've tried pretty much everything. I tried to simplify the code (below). I did get this far however:

void wakeUp() {
  detachInterrupt(wakeUpPin);
  Serial.println("In the wakeUp function");
}
void sleep() {
  Serial.println("In the sleep function");
  attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);  //LOW, CHANGE, RISING, FALLING
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);            //Power down
  detachInterrupt(wakeUpPin);
}

When I use the remote control button 1, it calls the "sleep" function. The TX LED remains on continuously and the 5v LED on pin 12 goes out. That's the behavior I want. After several seconds, the 5v LED comes back on but the TX led on the Uno remains on continuously and no longer flashes. The Uno is no longer responsive. Again, I want to be able to toggle the power down but don't know how to get there.

Again, I appreciate any help with this.

The following sketch demonstrates that --
1. Blinking of built-in LED of UNO is an indication that the UNO is not sleeping.

2. When the Power Button (address = 21) of the TV Remote Controller is pressed, the UNO enters into sleep mode.

3. When any Button/Power Button of the TV Controller is pressed, the UNO wakes up. (Allow wake-up time.)

4. When the Power Button (address = 21) of the TV Remote Controller is pressed again, the UNO enters into sleep mode again.

Sketch:

#include<IRremote.h>
#include<avr/sleep.h>
#define IR_RECEIVE_PIN 2     //IR Receiver
const int wakeUpPin = 2;
const int ledPin = LED_BUILTIN;  //DPin-13
int address;
volatile bool flag = false;

void setup()
{
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN);
  pinMode(ledPin, OUTPUT);
  pinMode(wakeUpPin, INPUT_PULLUP);
  SMCR |= (1 << SM1);  //power down mode
}

void loop()
{
  //Serial.println("oK"); //debugg
  if (IrReceiver.decode())
  {
    IrReceiver.resume();
    address = IrReceiver.decodedIRData.command;
    Serial.println(address, DEC);
    delay(2000);
    if (address == 21)//press Power Button of TV Remote Controller
    {
      if (flag == false)
      {
        IrReceiver.end();
        attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);
        bitSet(SMCR, SE);  //activate sleep mode
        sleep_cpu();  //asm: sleep; the MCU sleeps
        detachInterrupt(digitalPinToInterrupt(wakeUpPin));

        IrReceiver.resume();
        IrReceiver.begin(IR_RECEIVE_PIN);
        flag = true;
      }
      else
      {
        flag = false;
      }
    }
  }
  else
  { //indication that UNO is not sleeping
    digitalWrite(ledPin, HIGH);
    delay(50);
    digitalWrite(ledPin, LOW);
    delay(50);
  }
}

void wakeUp()
{

}

Well I can't thank you enough. The major difference I see is that you have assigned both the LED and the IR Receiver to the same pin (2). As a result of your answer here, I looked at the the Arduino Uno data sheet (452 pages!) and it appears the command you used:

SMCR |= (1 << SM1);

assigns the level of sleep to be assigned along with:

 bitSet(SMCR, SE);  //activate sleep mode
 sleep_cpu();  //asm: sleep; the MCU sleeps

I will be looking at these to get a better understanding.

Again, you took time to provide this answer. Your effort is appreciated. Thank you.

Although I'm happy with the way the code on the solution works and can live with it, there remains one issue. There is only one button (21) that will turn the LED off however any button on the remote will turn the LED on. Not only any button on this remote but any button on any remote in the room.

I've tried to control this with the "flag" setting, using the "wakeup" folder and other things too. Nothing works.

The sketch of post #11 works with some reservations. The sketch could be much more elegant for which we need to carefully draw the "State Machine Diagram" for which we may seek assistance from @J-M-L. The requirement is: only the Power Switch (address 21) of the Remote will put UNO into sleep and th next press of the Power Swicth will wake-up the UNO.

In TV Remote Controller, the same Power-down switch is used for On/Off the TV. No other switch can shut-off the power of the TV. The sketch of post #11 is not doing that reliably. The power switch of the remote controller does put the UNO into sleep mode; but, pressing of any key can wake-up the UNO which is no good. Moreover, the sketch sometimes hangs.

During listening session, the IR accepts the command from the Remote via "Digital Input DPin-2". If the incoming command is 21, the UNO enters into power-down sleep.

During sleeping, the DPin-2 is alert to accept external signal to wke-up the UNO. Any signal that comes from Remote at DPin-2 interrupts the UNO and it wakes-up. The built-in LED of the UNO Board is connected at DPin-13.

Although I'm happy with the way the code on the solution works and can live with it, there remains one issue. There is only one button (21) that will turn the LED off however any button on the remote will turn the LED on. Not only any button on this remote but any button on any remote in the room.

I've tried to control this with the "flag" setting, using the "wakeup" folder and other things too. Nothing works.

Can the DPin-3 (on the UNO) be introduced to address the issue of limiting the wakeup signal to only a single remote button.

Becuase it is only the external interrupt that can wake-up the UNO from sleep, we have to use either DPin-2 or DPin-3. Once the UNO is in sleep, the MCU is off (Fig-1) and is unable to decode the incoming IR signal.


Figure-1:

This is just an update of the results (sadly) of using this solution:

1 - As previously stated, re-enabling the Arduino after being powered down using the IR receiver allows any remote nearby to wake up the Arduino.

2 - Powering down the Arduino using the remote will not last for more than 30 minutes (in my experience). It wakens on its own or from some other unknown source.

3 - After awakening on its own, it is unresponsive to the remote control when trying to have it power down again. It's necessary to hit the Arduino reset button to have it respond to the remote.

I'd still like to control the Arduino using the remote but I will now explore using a mosfet to turn the power off. As another alternative, I'm wondering if I can put a ATTiny88 between the battery and the Arduino Uno to control the power to the Uno.

Again, thank you for your time and expertise. I'm very appreciative.

If only the Power Button of TV Remote Controller can turn-On and Turn-Off TV power, then it should do the same thing for the UNO. Therefore, working on refining the sketch of post #11 to achieve this goal would be an appropriate task.

Your logic makes perfect sense but in actuality the On/Off button (address = 2) not 21, turns off the Arduino but doesn't turn it back on. Any other remote control button does, however.

As you suggest, I'll give this another look.