1

Question (TL;DR): what is the optimal method to deep sleep (to run an ATtiny45 or ATmega on batteries for 1+ year) but still be able to detect a button press? Is it possible to deep sleep until a button is pressed?

I'm using a classic "debounce" method to detect a button press:

int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0; 
unsigned long debounceDelay = 50;  

void setup() {
  pinMode(2, INPUT);
}

void loop() {
  int reading = digitalRead(2);
  if (reading != lastButtonState) { lastDebounceTime = millis(); }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == HIGH) {
         // Button pressed, do something here! 
         // In my actual code, it sends a packet via RF 433 Mhz 
      }
    }
  }
  lastButtonState = reading;
  // snore(20);          // TinySnore here
}

My ATtiny45 should run during 1 year on 3 AA batteries, so I added a deep sleep using TinySnore. It works, and made the consumption go from more than 1mA to 0.2mA (@1 Mhz).

I know it's possible to go down to 5 µA if I snore(1000); but then obviously it won't be able to detect a button press.

I tried various sleep values: snore(10);, snore(20);, snore(50);, etc. but I didn't get consistent results.

Thus the question: how to deep sleep until a button is pressed?


I was thinking about using attachInterrupt() but then what kind of sleep should we use? snore()? delay()?

3

ATTINY's are designed to easily do this since they can wake from power down sleep mode by a pin change...

enter image description here

Basically you want to...

  1. Connect a normally-open push button between an IO pin and ground.
  2. Enable the pull-up on the IO.
  3. Enable the pin change interrupt on the IO pin.
  4. Enable interrupts.
  5. Enter "power down" sleep mode.

When the button is pushed, it will connect the IO pin to ground, which will cause a pin change interrupt, which will wake the chip from sleep.

Note that you do not need to do anything in the pin change ISR (you can use the EMPTY_INTERRUPT compiler macro). If the ISR is empty, execution will pick up on the line after the sleep upon waking.

While in deep sleep, the chip will use an astonishingly small amount of power. For example, an ATTINY25/45/85 running off of a 3V coin cell at room temp will use less than 0.2uA...

enter image description here

A typical CR2032 coin cell is rated for 235mAh to 2V, so at this rate we'd expect to be able to wait for a button press like this for 100+ years.

IRL the battery will likely self discharge and physically deteriorate long before then (rated at 1% per year), but still not too shabby.

enter image description here

0

First I was unable to make the intterupt work for multiple pins, but finally here is a working code:

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

ISR(PCINT0_vect) {
  if (digitalRead(0) == LOW)            # PB0 = pin 5 pressed => LED on
    digitalWrite(4, HIGH);
  else if (digitalRead(1) == LOW)       # PB1 = pin 6 pressed => LED off
    digitalWrite(4, LOW);
  else if (digitalRead(2) == LOW)       # PB2 = pin 6 pressed => LED on
    digitalWrite(4, HIGH);
}

void setup() {  
  pinMode(4,OUTPUT); // LED
  pinMode(0,INPUT_PULLUP);
  pinMode(1,INPUT_PULLUP);
  pinMode(2,INPUT_PULLUP);
  ADCSRA = 0; // ADC disabled
  GIMSK = 0b00100000;  // General Interrupt Mask Register, / Bit 5 – PCIE: Pin Change Interrupt Enable / When the PCIE bit is set (one) and the I-bit in the Status Register (SREG) is set (one), pin change interrupt is enabled. Any change on any enabled PCINT[5:0] pin will cause an interrupt. The corresponding interrupt of Pin Change Interrupt Request is executed from the PCI Interrupt Vector. PCINT[5:0] pins are enabled individually by the PCMSK0 Register. / see https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
  PCMSK = 0b00000111;  
} 

void loop() {
  sleep_enable();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
  sleep_cpu(); 
}

Note: PCINTs are handled by a single PCINT_vect here.

PS: as mentioned by Maximilian in a comment, this post gives nice solutions: Detecting key presses on a keypad whilst asleep.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.