Help with correct implementation of Sleep and Interrupt

Hello all! I am trying to build an Arduino watch, and as you can imagine I will be using a small battery. I plan on using the sleep capabilities of the arduino, and had a couple of questions.

  1. I know I can attach a Interrupt Service Routine (whcih to me is just a function) to pins 2 and 3 - and to all pins if I use a library, right?. Is there a way to wake up the arduino with the press of a button and for it to execute the loop() until it is depressed? I used millis to debounce the buttons, and all of that is in the loop. After it I would like it to go to sleep.

  2. In the case that I cant run loop for the duration of the press, what would be the best way to do it. Can I use millis in the ISR, if I need to use one?

I have all these button 'if digitalRead(Button == HIGH) {...' statements and just want to put to sleep the arduino and wake it up when needed. Do I necessarily have to transfer the if statements from the loop to the ISR?

I also have a button 'if statement 'that measures the duration of the press, and don't know how it would work with the interrupts. Any help will be really appreciated!

Thanks for all your help!

The button (an external interrupt) can wake the processor up, so the "button push" ISR doesn't need to do anything at all except exit.

The processor will remain active until put to sleep again.

Be sure to study Nick Gammon's tutorials on power saving.

jremington:
The button (an external interrupt) can wake the processor up, so the “button push” ISR doesn’t need to do anything at all except exit.

The processor will remain active until put to sleep again.

Be sure to study Nick Gammon’s tutorials on power saving.

Thanks for your help jremington! I gave it a shot and wrote this code:

#include <avr/sleep.h>


//Library for Display

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//Code for debounce

int debounceCounter111 = 0;
int buttonState;
int lastButtonState = HIGH;

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

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

void sleep()
{
  // disable ADC
ADCSRA = 0;

set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();

// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts ();

// will be called when pin D2 goes low
attachInterrupt (0, wake, FALLING);
EIFR = bit (INTF0);  // clear flag for interrupt 0

// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);

// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts ();  // one cycle
sleep_cpu ();   // one cycle
}

void displayText(String activityName)
{
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.clearDisplay();
  display.println(activityName);
  display.display();
  delay(200);
  display.clearDisplay();
  display.display();
}

int buttonTwo = 2;

void setup ()
{
pinMode(buttonTwo, INPUT_PULLUP);

//   DISPLAY SETUP
      // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2000);

  // Clear the buffer.
  display.clearDisplay();
  display.display();

}

void loop()
{

int reading = digitalRead(buttonTwo);

  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == LOW) {
        ++debounceCounter111;


        if (debounceCounter111 > 3){
          debounceCounter111 = 1;
          displayText("Activity 1");
          sleep();
        }

        if (debounceCounter111 == 1){
          // Code to display something on OLEd
          displayText("Activity 1");
          sleep();
        }

        if (debounceCounter111 == 2){
          // Code to display something on OLEd
          displayText("Activity 2");
          sleep();

        }
        if (debounceCounter111 == 3){
          // Code to display something on OLEd
          displayText("Activity 3");
          sleep();

        }




      }
    }
  }

}

For some reason it is not working: The OLED is not displaying anything which means the sleep() function is not working either. DO you have an idea of what’s going on?

Thanks!

Please read the "How to use this forum" post and post your code properly.

Many people (including me) won't download something from an unknown website.

jremington:
Please read the "How to use this forum" post and post your code properly.

Many people (including me) won't download something from an unknown website.

Sorry! I realised my error just after I hit 'Post'. It's fixed now :slight_smile:

Be sure that the OLED display is working properly, before implementing sleep functions.

What troubleshooting have you done for the individual parts of the code?

  • Is the button wired properly?
  • Have you verified that the OLED can display something with a simple sketch (just print text to it an nothing else)?
  • Does the sketch work without the sleep functions?

jremington:
Be sure that the OLED display is working properly, before implementing sleep functions.

It is working now! It was drawing 7ma awake, and now it is down to 3ma - I still have the power led which I suspect draws 2ma or so.

However, a new problem has appeared: When I have the arduino in sleep mode, I push the button once and it wakes up, and I have to push it again to register a push according to the loop. I would like to modify it so that if it is asleep, I push it once and it wakes it up and registers it as a push, both with just one push of button. How could I modify my code?

This is the final code that worked:

//Library for sleeping
#include <avr/sleep.h>


//Library for Display - Can skip it if you want!

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

String activity1 = "Work";

//Variable setup fot Debounce

int debounceCounter111 = 0;
int buttonState;
int lastButtonState = HIGH;

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

//ISR to call when button is pressed and wants to interrupt.

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


//Function to send it to sleep.
void sleep()
{
  // disable ADC
ADCSRA = 0;

set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();

// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts ();

// will be called when pin D2 goes low
attachInterrupt (0, wake, FALLING);
EIFR = bit (INTF0);  // clear flag for interrupt 0

// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);

// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts ();  // one cycle
sleep_cpu ();   // one cycle
}


//Display text on screen fucntion - You can skip.

void displayText(String activityName)
{
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.clearDisplay();
  display.println(activityName);
  display.display();
  delay(1100);
  display.clearDisplay();
  display.display();
}

//Button Varibale int

int buttonTwo = 2;

///////++++++++++++++++++++SETUP++++++++++++++++++++++///

void setup ()
{

//digitalWrite (2, HIGH);  // enable pull-up
pinMode(buttonTwo, INPUT_PULLUP);

//DISPLAY SETUP - You can skip

      // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2000);

  // Clear the buffer.
  display.clearDisplay();
  display.display();

}

//+++++++++++++++++++++++++++++LOOP+++++++++++++++++++++++++++//

void loop()
{

int reading = digitalRead(buttonTwo);

  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == LOW) {
        ++debounceCounter111;


        if (debounceCounter111 > 3){
          debounceCounter111 = 1;
          displayText("Activity 1");
          sleep();
        }

        if (debounceCounter111 == 1){
          // Code to display something on OLEd
          displayText(activity1);
          sleep();
        }

        if (debounceCounter111 == 2){
          // Code to display something on OLEd
          displayText(activity1);
          sleep();

        }
        if (debounceCounter111 == 3){
          // Code to display something on OLEd
          displayText(activity1);
          sleep();

        }
      }
    }
  }

    lastButtonState = reading;
}

Jiggy-Ninja:
What troubleshooting have you done for the individual parts of the code?

  • Is the button wired properly?
  • Have you verified that the OLED can display something with a simple sketch (just print text to it an nothing else)?
  • Does the sketch work without the sleep functions?

Thanks for your suggestions; I managed to make it work btu have a new problem! See above :slight_smile:

If the processor wakes up, the button was pushed.
Do whatever is required to register the push.