user38469: 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()?

user38469: 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.

user1576: I would put the MCU in deep sleep and then use the reset button.

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

Connect a normally-open push button between an IO pin and ground.
Enable the pull-up on the IO.
Enable the pin change interrupt on the IO pin.
Enable interrupts.
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...

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.


38469: <p><strong>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?</strong> Is it possible to deep sleep <em>until</em> a button is pressed?</p>

<p>I'm using a classic "debounce" method to detect a button press:</p>

<pre><code>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) &gt; 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
}
</code></pre>

<p>My ATtiny45 should run during 1 year on 3 AA batteries, so I added a deep sleep using <a href="https://github.com/connornishijima/TinySnore/blob/master/src/tinysnore.cpp" rel="noreferrer">TinySnore</a>. It works, and made the consumption go from more than 1mA to 0.2mA (@1 Mhz).</p>

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

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

<p>Thus the question: <strong>how to deep sleep <em>until</em> a button is pressed?</strong></p>

<hr>

<p>I was thinking about using <a href="https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/" rel="noreferrer"><code>attachInterrupt()</code></a> but then what kind of sleep should we use? <code>snore()</code>? <code>delay()</code>?</p>
 38469: <p>First I was <a href="https://arduino.stackexchange.com/questions/70292/how-to-assign-an-interrupt-to-a-button-press-with-an-attiny-interrupt-not-fire">unable to make the intterupt work for multiple pins</a>, but finally here is a working code:</p>
<pre><code>#include &lt;avr/interrupt.h&gt;
#include &lt;avr/sleep.h&gt;

ISR(PCINT0_vect) {
  if (digitalRead(0) == LOW)           // # PB0 = pin 5 pressed =&gt; LED on
    digitalWrite(4, HIGH);
  else if (digitalRead(1) == LOW)      // # PB1 = pin 6 pressed =&gt; LED off
    digitalWrite(4, LOW);
  else if (digitalRead(2) == LOW)      // # PB2 = pin 6 pressed =&gt; 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(); 
}
</code></pre>
<p>Note: <a href="https://arduino.stackexchange.com/questions/70348/pcint0-pcint1-pcint2-etc-on-attiny45-85/70351#70351">PCINTs are handled by a single PCINT_vect</a> here.</p>
<p>PS: as mentioned by Maximilian in a comment, this post gives nice solutions: <a href="https://www.gammon.com.au/forum/?id=11497&amp;reply=4#reply4" rel="nofollow noreferrer">Detecting key presses on a keypad whilst asleep</a>.</p>
 1576: <p>I would put the MCU in deep sleep and then use the reset button.</p>
 7859: <p>ATTINY's are designed to easily do this since they can wake from <code>power down</code> sleep mode by a pin change...</p>

<p><a href="https://i.stack.imgur.com/8dnWb.png" rel="noreferrer"><img src="https://i.stack.imgur.com/8dnWb.png" alt="enter image description here"></a> </p>

<p>Basically you want to...</p>

<ol>
<li>Connect a normally-open push button between an IO pin and ground.</li>
<li>Enable the pull-up on the IO.</li>
<li>Enable the pin change interrupt on the IO pin.</li>
<li>Enable interrupts.</li>
<li>Enter "power down" sleep mode.</li>
</ol>

<p>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.</p>

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

<p>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...</p>

<p><a href="https://i.stack.imgur.com/2BkpN.png" rel="noreferrer"><img src="https://i.stack.imgur.com/2BkpN.png" alt="enter image description here"></a></p>

<p>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 <a href="https://frinklang.org/fsp/frink.fsp?fromVal=%28235%20milli%20amp%20hours%29%20%2F%20%280.2%20microamp%29&amp;toVal=years#calc" rel="noreferrer">100+ years</a>.</p>

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

<p><a href="https://i.stack.imgur.com/26ahf.png" rel="noreferrer"><img src="https://i.stack.imgur.com/26ahf.png" alt="enter image description here"></a></p>
