ATTiny85 Wake from Sleep on Pin State Change Code Example

I spent an inordinate amount of time trying to figure out how put my ATTiny85 to sleep and then wake it up when the state of a pin changes. So I’ll document my test code in case anyone else goes searching for a simple example.

The reference I ended up using is:

Unfortunately, even after reading through the forum thread the final working code was evidently never posted. However, there was enough here for me to figure out the problem and get it running.

The following code is very simple. It flashes the LED quickly to let you know the program is started. It then sets up for sleep mode and goes to sleep. When the button is pushed, it awakes, flashes the LED once and goes back to sleep.

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

const int switchPin                     = 3;
const int statusLED                     = 2;

void setup() {

    pinMode(switchPin, INPUT);
    digitalWrite(switchPin, HIGH);
    pinMode(statusLED, OUTPUT);

    // Flash quick sequence so we know setup has started
    for (int k = 0; k < 10; k = k + 1) {
        if (k % 2 == 0) {
            digitalWrite(statusLED, HIGH);
        else {
            digitalWrite(statusLED, LOW);
        } // for
    } // setup

void sleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT3);                   // Use PB3 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT3);                  // Turn off PB3 as interrupt pin
    sleep_disable();                        // Clear SE bit
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
    } // sleep

ISR(PCINT0_vect) {
    // This is called when the interrupt occurs, but I don't need to do anything in it

void loop() {
    digitalWrite(statusLED, HIGH);
    digitalWrite(statusLED, LOW);

One additional comment: in my actual program, I will need to awake if either of two buttons is pressed. To handle this, I simply add these additional statements below their near twins:

    PCMSK |= _BV(PCINT4);                   // Use PB4 as interrupt pin
    PCMSK &= ~_BV(PCINT4);                  // Turn off PB4 as interrupt pin


This entry was posted in c-tinys and tagged . Bookmark the permalink.

18 Responses to ATTiny85 Wake from Sleep on Pin State Change Code Example

  1. Matt N says:

    Thanks for posting this, it really helped me. I think I’ve been following the same forum posts as you but I don’t have as good of a grasp of AND/OR’ing registers as you do, so seeing the code like this is perfect.

    The regular Arduino PinChangeInt library is 3kb but your solution is only a few lines, much better for the ATtiny!

  2. Sean Straw says:

    On uCs with limited memory, it is not uncommon to need to forego the complete library implementation of something and raid the library for the minimalistic functionality necessary to accomplish the needed task.

    Other examples include LCD (quite piggy), random (brings in 700-900 bytes of overhead, which makes it almost unusable on an ATTiny2x with just 2K of flash, but for “random-ish”, there are other things you can do that aren’t as costly), long and floating point operations (which can sometimes be circumvented by changing how you perform math operations and what sort of precision you really need), and delay().

    Delay brings in about 200 bytes of timer code that you can save by doing the following:

    myDelay( int seconds )
    while ( seconds– )
    delayMicroseconds( 1000 );

    This won’t be quite as precise, but for most purposes (“wait ‘x’ seconds before re-arming”), is more than sufficient, and the memory saved on the uC can be used for something more critical.

    Long math is slow enough that even on an 8MHz (no external crystal necessary) ATTiny8x, driving a pin high, delaying a fixed time (with delaymicroseconds()), driving the pin low, delaying, and doing long math to decrement a counter in a for loop results in a noticeable “click” in the output. See the code in Determining the delay and number of iterations Using an int before entering the loop avoids that,

    As I develop a programs, I test the builds and note the build size (for a given target, so size is apples-to-apples: if you change uC targets, binary sizes can change significantly), then note these either within comments within the module, or in the git commit comments (changes are pushed to a git repo regularly). Doing so, you easily visualize how much some changes add to your code size, and that helps a lot when you’re working with limited program space.

    • xunker says:

      Excellent insight, Sean! I love it.

      Even though I consider myself an experienced programmer (15 years professionally), all my skill is in very-high-level languages on machines megabytes/gigabytes/terabytes of memory and storage, so shifting designs to focus on individual bytes is a huge — but rewarding — change in focus. Saving 200 bytes by rolling your own delay() function is something that would have never occurred to me.

      • Dan TheMan says:

        I started programming in the late 70’s in what would have been considered high-level then (Fortran and COBOL) but we still had to watch every byte we used. Learning to program microcontrollers has been like going back to my programming roots. Every byte, and every instruction counts and there aren’t any fancy debugging tools. 🙂

  3. Sean Straw says:

    I started programming in assembler on Z80 (CP/M) followed by 6502 CPUs (Apple and Atari), and there you had at best 64KB of memory total for the entire system, including the OS (and the ROM). Things improved with the x86, though having gotten used to writing compact code, all that extra memory often went unused (often, one used extra memory to buffer things for increased performance, but because every PC had a different memory config, apps needed to run on the lower configs if you wanted them to be usable by a large audience).

    Despite developing using higher level languages (C, C++, Perl, Ruby (ugh, did I just vomit a little?), etc) for most things these days, memory consumption still remains important. Poorly written OOP code can chew through memory. Some languages are versatile – I’ve found PHP to be nice for web dev, but dang, it’s memory representation of some structures is gross. The venerable 8051 uC is still in fairly common use, and while it only has 16 bit addressing (64KB addressable space), it has different banks of memory (data, and program being the chief ones in use), so program is in 64K, data can be in a separate 64K. Using external address line switching and linker trickery, you can bank switch ROM to increase program size on the 8051. It’d been years since I did any 8051 dev, but as an ASIC at my employer utilizes the 8051 core, I recently got back to it for some projects, though otherwise most of my work is on an ARM based embedded system.

    I’ve used the ATTiny24V for some projects – it has just 2K of flash. I have some ATTiny13A uCs (which sport just 1K of flash), but have yet to use them in anything (and, arguably, they’re not so much cheaper than the ’85 that it really makes sense to use them, outside of some lower voltage capabilities).

    I’m eager to check out the ATTiny43U. 4K flash, and it has an integrated boost converter capability: it can drive itself off of a sub 1.5V battery *AND* reportedly drive some external loads once bootstrapped. Obviously though, it has a different pinout than its x4 and x5 ATTiny siblings, so won’t be a drop-in design. For some of my own projects, I have to use a boost module I designed, which charges an UltraCap, but I don’t have a control in place to shut off the boost when the Cap is above a target voltage (that’s what I planned to use the ATTiny13A’s for) – integrating this directly into the uC is really attractive.

    Personally, my next step with the AVR is to switch or tweak development toolchains – the Arduino IDE is entirely too simplistic and limited — as it is, I set the option to use an external editor, and just use the IDE to launch the compiler and drive the binary through my programmer devices (AVRisp or Arduino-as-ISP with a ICSP ZIF shield I made). The gnu toolchain (c/c++ compiler, assembler, library tools, and linker – though the versions distributed in the Arduino IDE are fairly dated), is still underneath the IDE, so it’s a matter of generating standard makefiles for projects and building libs with assembler, then the IDE can be ditched. AVR Studio 6.x is an alternative, but sadly, it’s purely a windows offering.

  4. Pingback: Low Power ATTiny85 Experiment | Big Dan the Blogging Man

  5. José says:

    Hello, thank you for your code. Can i use the same code for an Attiny84 ? Thanks.

    • Dan TheMan says:

      Looking at the datasheet, (, I think it would work.

      I do not think you would hurt the 84 if it doesn’t work.

    • Sean Straw says:

      84 and 85 should differ only in the number of I/O (and their various capabilities), so the sleep code should be fine. Other members of the AVR family (such as the Tiny13A or 24) may not support all of the same Watchdog intervals, but those aren’t employed here in Dan’s example (a watchdog timout would be how you could sleep and wake periodically to check something, not just in response to an external event).

  6. Dan TheMan says:

    It’s always cool to see others jump in and help. Thanks guys!

  7. KW says:

    This was great! Definately saved me a couple of hours, thanks!
    2 small points to mention:
    1. I use Proteus for simulation and it doesn’t like delaymicroseconds() for whatever reason. Yes, It is buggy but it was worth the money…I’ve used it to develop dozens of projects.
    2. I adding a boolean (bRelease) initialized HIGH so you only trigger on button closure, not both close and open:
    sleep(); // sleep until change of state
    if (bReleased) { // is button in released state (open)
    bReleased = LOW; // button is now in pressed state (closed)
    digitalWrite(LED, HIGH); // pulse LED
    digitalWrite(LED, LOW);
    else { // button was in presse state so has been released
    bReleased = HIGH; // so set flag and go take a nap

  8. Pingback: AtTiny85 put in sleep mode and awaken through a 4N35 optocoupler | curiosityBikes

  9. Pingback: Arduino ile Attiny85 Programlama Guncel | Genel | Burak Öztürk

  10. Pingback: [ASK] arduino - ATtiny85 (Sparfun Clone/Trinket), detect/read operation voltage | Some Piece of Information

  11. zach says:

    Hey thanks for the simple and concise code, just what a beginner like me was looking for for my project!!


  12. imginit says:

    Thank you for the great tutorial. I got this to work with the ATTiny84 by making small edits. Here’s my code:

    void sleep() {

    GIMSK |= _BV(PCIE0); // Enable Pin Change Interrupts
    PCMSK0 |= _BV(PCINT3); // Use PB3 as interrupt pin
    PCMSK0 |= _BV(PCINT4); // Use PB4 as interrupt pin
    ADCSRA &= ~_BV(ADEN); // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); // replaces above statement

    sleep_enable(); // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei(); // Enable interrupts
    sleep_cpu(); // sleep

    cli(); // Disable interrupts
    PCMSK0 &= ~_BV(PCINT3); // Turn off PB3 as interrupt pin
    PCMSK0 &= ~_BV(PCINT4); // Use PB4 as interrupt pin
    sleep_disable(); // Clear SE bit
    ADCSRA |= _BV(ADEN); // ADC on

    sei(); // Enable interrupts
    } // sleep

    Switched the PCIE to PCIE0 and PCMSK to PCMSK0.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s