├── README.md └── arduino_deep_sleep.ino /README.md: -------------------------------------------------------------------------------- 1 | [![license-badge][]][license] [![stars][]][stargazers] ![hit-count] [![github-issues][]][issues] 2 | 3 | # Arduino_Deep_Sleep 4 | 5 | An Arduino example of the proper and most advanced way to put any AVR MCU into sleep 6 | 7 | This example puts AVR MCU in deep sleep for 30 seconds. Wakes it up, turns on/off LED and returns Arduino back to sleep. Average ATtiny85 consumption in deep sleep mode, achieved by this sketch, is ~3 microamperes at 3.3 volt. 8 | 9 | [license-badge]: https://img.shields.io/badge/License-GPLv3-blue.svg 10 | [license]: https://choosealicense.com/licenses/gpl-3.0/ 11 | [stars]: https://img.shields.io/github/stars/enjoyneering/Arduino_Deep_Sleep.svg 12 | [stargazers]: https://github.com/enjoyneering/Arduino_Deep_Sleep/stargazer 13 | [hit-count]: https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fenjoyneering%2FArduino_Deep_Sleep&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false 14 | [github-issues]: https://img.shields.io/github/issues/enjoyneering/Arduino_Deep_Sleep.svg 15 | [issues]: https://github.com/enjoyneering/Arduino_Deep_Sleep/issues/ 16 | -------------------------------------------------------------------------------- /arduino_deep_sleep.ino: -------------------------------------------------------------------------------- 1 | /***************************************************************************************************/ 2 | /* 3 | Arduino example of the proper and most advanced way to put any AVR based Arduino 4 | boards into sleep 5 | 6 | Sketch puts Arduino in to the deep sleep for ~30 sec. Wakes it up, turns on/off LED connected 7 | to pin0/pin13 & puts Arduino back to sleep. 8 | 9 | written by : enjoyneering79 10 | sourse code: https://github.com/enjoyneering/Arduino_Deep_Sleep 11 | 12 | GNU GPL license, all text above must be included in any redistribution, see link below for details: 13 | - https://www.gnu.org/licenses/licenses.html 14 | */ 15 | /***************************************************************************************************/ 16 | #include //AVR MCU power management 17 | #include //disable/anable AVR MCU peripheries (Analog Comparator, ADC, USI, Timers/Counters) 18 | #include //AVR MCU watchdog timer 19 | #include //includes the apropriate AVR MCU IO definitions 20 | #include //manipulation of the interrupt flags 21 | 22 | #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) 23 | #define LED 0 24 | #else 25 | #define LED LED_BUILTIN 26 | #endif 27 | 28 | /**************************************************************************/ 29 | /* 30 | variables 31 | */ 32 | /**************************************************************************/ 33 | volatile byte watchdogCounter = 0; 34 | 35 | /**************************************************************************/ 36 | /* 37 | all_pins_output() 38 | 39 | Sets all pins as output, to prevent the MCU from waking up from a 40 | random noise signal during sleep, use if needed 41 | */ 42 | /**************************************************************************/ 43 | void all_pins_output() 44 | { 45 | for (byte pin = 0; pin < 5; pin++) //ATtiny85 has 5 pins 46 | { 47 | pinMode(pin, OUTPUT); 48 | } 49 | } 50 | 51 | /**************************************************************************/ 52 | /* 53 | all_pins_input() 54 | 55 | Sets back all pins as input 56 | 57 | NOTE: 58 | - INPUT_PULLUP connects internal 20k..30k pullups resistors & increase 59 | consumption by 1.25uA 60 | */ 61 | /**************************************************************************/ 62 | void all_pins_input() 63 | { 64 | for (byte pin = 0; pin < 5; pin++) //ATtiny85 has 5 pins 65 | { 66 | pinMode(pin, INPUT); 67 | //pinMode(pin, INPUT_PULLUP); 68 | } 69 | } 70 | 71 | /**************************************************************************/ 72 | /* 73 | setup() 74 | 75 | Main setup 76 | 77 | NOTE: 78 | - valid sleep intervals: WDTO_15MS, WDTO_30MS, WDTO_60MS, WDTO_120MS 79 | WDTO_250MS, WDTO_500MS, WDTO_1S, WDTO_2S 80 | WDTO_4S, WDTO_8S 81 | */ 82 | /**************************************************************************/ 83 | void setup() 84 | { 85 | pinMode(LED, OUTPUT); 86 | setup_watchdog(WDTO_8S); //approximately 8 sec. of sleep 87 | } 88 | 89 | /**************************************************************************/ 90 | /* 91 | loop() 92 | 93 | Main loop 94 | */ 95 | /**************************************************************************/ 96 | void loop() 97 | { 98 | while (watchdogCounter < 4) //wait for watchdog counter reached the limit, WDTO_8S * 4 = 32sec. 99 | { 100 | //all_pins_output(); 101 | arduino_sleep(); 102 | } 103 | 104 | //wdt_disable(); //disable & stop wdt timer 105 | watchdogCounter = 0; //reset counter 106 | 107 | power_all_enable(); //enable all peripheries (ADC, Timer0, Timer1, Universal Serial Interface) 108 | /* 109 | power_adc_enable(); //enable ADC 110 | power_timer0_enable(); //enable Timer0 111 | power_timer1_enable(); //enable Timer1 112 | power_usi_enable(); //enable the Universal Serial Interface module 113 | */ 114 | delay(5); //to settle down ADC & peripheries 115 | 116 | digitalWrite(LED, HIGH); //led blink 117 | delay(3000); 118 | digitalWrite(LED, LOW); 119 | 120 | //wdt_enable(WDTO_8S); //enable wdt timer 121 | } 122 | 123 | /**************************************************************************/ 124 | /* 125 | arduino_sleep() 126 | 127 | Puts system into the sleep state 128 | 129 | NOTE: 130 | - there are 6 different sleeps modes: 131 | * SLEEP_MODE_IDLE - the least power savings (CPU stopped but Analog Comparator, ADC, USI, Timer/Counter, 132 | Watchdog (if enabled), and the interrupt system to continue operating) (default) 133 | * SLEEP_MODE_ADC - ADC Noise Reduction (CPU stopped but the ADC, the external interrupts, 134 | and the Watchdog (if enabled) to continue operating) 135 | * SLEEP_MODE_PWR_SAVE - supported by atiny25, atiny45, atiny85 136 | * SLEEP_MODE_EXT_STANDBY - not supported by atiny25, atiny45, atiny85 137 | * SLEEP_MODE_STANDBY - not supported by atiny25, atiny45, atiny85 138 | * SLEEP_MODE_PWR_DOWN - the most power savings: all oscillators are stopped, only an External Reset, Watchdog Reset, 139 | Brown-out Reset, USI start condition interupt, an external level interrupt on INT0 or a pin 140 | change interrupt can wake up the MCU. 141 | - another useful functions: 142 | * sleep_enable() - enable sleep, set Sleep Enable (SE) bit 143 | * sleep_cpu() - system stops & sleeps here, but doesn't automatically sets/clears Sleep Enable (SE) bit like sleep_mode() 144 | * sleep_disable() - disable sleep, clears Sleep Enable (SE) bit 145 | 146 | */ 147 | /**************************************************************************/ 148 | void arduino_sleep() 149 | { 150 | cli(); //disable interrupts for time critical operations below 151 | 152 | power_all_disable(); //disable all peripheries (ADC, Timer0, Timer1, Universal Serial Interface) 153 | /* 154 | power_adc_disable(); //disable ADC 155 | power_timer0_disable(); //disable Timer0 156 | power_timer1_disable(); //disable Timer2 157 | power_usi_disable(); //disable the Universal Serial Interface module 158 | */ 159 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set sleep type 160 | 161 | #if defined(BODS) && defined(BODSE) //if MCU has bulit-in BOD it will be disabled, ATmega328P, ATtiny85, AVR_ATtiny45, ATtiny25 162 | sleep_bod_disable(); //disable Brown Out Detector (BOD) before going to sleep, saves more power 163 | #endif 164 | 165 | sei(); //re-enable interrupts 166 | 167 | sleep_mode(); /* 168 | system stops & sleeps here, it automatically sets Sleep Enable (SE) bit, 169 | so sleep is possible, goes to sleep, wakes-up from sleep after interrupt, 170 | if interrupt is enabled or WDT enabled & timed out, than clears the SE bit. 171 | */ 172 | 173 | /*** NOTE: sketch will continue from this point after sleep ***/ 174 | } 175 | 176 | /**************************************************************************/ 177 | /* 178 | setup_watchdog() 179 | 180 | Sets up watchdog to trigger interrupt, not reset, after watchdog is 181 | timed out 182 | 183 | NOTE: 184 | - WDT runs from internal 128kHz clock & continues to work during the 185 | deepest sleep modes to provide a wake up source 186 | - Your can configure watchdog timer through Watchdog Timer Control 187 | Register WDTCSR/WDTCR 188 | - Watchdog timer control register has seven configuration bits: 189 | 7 6 5 4 3 2 1 0 190 | WDIF, WDIE, WDP3, WDCE, WDE, WDP2, WDP1, WDP0 191 | - Following prescaler 4 bits determine how long the timer will count 192 | for before resetting: 193 | WDP3 WDP2 WDP1 WDP0 Time-out Macro 194 | 0 0 0 0 16ms WDTO_15MS 195 | 0 0 0 1 32ms WDTO_30MS 196 | 0 0 1 0 64ms WDTO_60MS 197 | 0 0 1 1 125ms WDTO_120MS 198 | 0 1 0 0 250ms WDTO_250MS 199 | 0 1 0 1 500ms WDTO_500MS 200 | 0 1 1 0 1000ms WDTO_1S 201 | 0 1 1 1 2000ms WDTO_2S 202 | 1 0 0 0 4000ms WDTO_4S 203 | 1 0 0 1 8000ms WDTO_8S 204 | - WDE bit Watchdog System Reset Enable, it is overridden by WDRF bit 205 | in MCUSR/MCU Status Register. This means that WDE is always set 206 | when WDRF is set. To clear WDE, WDRF must be cleared first. This 207 | feature ensures multiple resets during conditions causing failure 208 | & safe start-up after the failure 209 | - WDCE bit is Watchdog Change Enable, it is used in timed sequences for 210 | changing WDE & prescaler bits. To clear the WDE bit or change 211 | prescaler bits, WDCE must be set. Once written to one, hardware will 212 | clear WDCE after four clock cycles. 213 | - WDIE bit is Watchdog Interrupt Enable. When this bit is written to 214 | one & I-bit in MCUSR is set, the Watchdog Interrupt is enabled. 215 | If WDE is cleared in combination with this setting, the Watchdog Timer 216 | is in Interrupt Mode & corresponding interrupt is executed if time-out 217 | in the Watchdog Timer occurs. 218 | If WDE is set, the Watchdog Timer is in Interrupt & System Reset Mode. 219 | The first time-out in the Watchdog Timer will set WDIF. 220 | Executing corresponding interrupt vector will clear WDIE & WDIF 221 | automatically by hardware, Watchdog goes to System Reset Mode. 222 | This is useful for keeping the Watchdog Timer security while using 223 | interrupt. To stay in Interrupt & System Reset Mode, WDIE must be set 224 | after each interrupt. This shouldn't be done within interrupt 225 | service routine itself, as this might compromise the safety-function 226 | of Watchdog System Reset mode. If the interrupt is not executed before 227 | next time-out, a System Reset will be applied. 228 | - Watchdog Timer Configuration: 229 | WDTON WDE WDIE Mode Action Time-out Action 230 | 1 0 0 Stopped None 231 | 1 0 1 Interrupt Interrupt 232 | 1 1 0 System Reset Reset 233 | 1 1 1 Interrupt & Interrupt then go to System Reset 234 | System Reset 235 | 0 x x System Reset Reset 236 | WDTON fuse set to "0" means programmed & "1" means unprogrammed 237 | - WDIF is Watchdog System Interrupt Flag & automatically flagged high 238 | and low by the system if interrupt happend. 239 | - MCUSR is MCU Status Register, it provides information on which reset 240 | source caused an MCU reset. MCUSR bit 3 is WDRF Watchdog System Reset 241 | Flag. This bit is set if a Watchdog System Reset occurs. The bit is 242 | reset by a Power-on Reset, or by writing a logic zero to the flag. 243 | - Watchdog is a timer, if you donʼt reset it regularly it will time-out. 244 | Just call "wdt_reset()" before timer expires, otherwise a watchdog 245 | will reset MCU. 246 | - Macro _BV(bit)=(1 << (bit)) converts bit number into a byte value. 247 | */ 248 | /**************************************************************************/ 249 | void setup_watchdog(byte sleep_time) 250 | { 251 | cli(); //disable interrupts for time critical operations below 252 | 253 | wdt_enable(sleep_time); //set WDCE, WDE change prescaler bits 254 | 255 | MCUSR &= ~_BV(WDRF); //must be cleared first, to clear WDE 256 | 257 | #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) 258 | WDTCR |= _BV(WDCE) & ~_BV(WDE); //set WDCE first, clear WDE second, changes have to be done within 4-cycles 259 | WDTCR |= _BV(WDIE); //set WDIE to Watchdog Interrupt 260 | #else 261 | WDTCSR |= _BV(WDCE) & ~_BV(WDE); //set WDCE first, clear WDE second, changes have to be done within 4-cycles 262 | WDTCSR |= _BV(WDIE); //set WDIE to Watchdog Interrupt 263 | #endif 264 | 265 | sei(); //re-enable interrupts 266 | } 267 | 268 | /**************************************************************************/ 269 | /* 270 | ISR(WDT_vect) 271 | 272 | Watchdog Interrupt Service Routine, executed when watchdog is timed out 273 | 274 | NOTE: 275 | - if WDT ISR is not defined, MCU will reset after WDT 276 | */ 277 | /**************************************************************************/ 278 | ISR(WDT_vect) 279 | { 280 | watchdogCounter++; 281 | } 282 | --------------------------------------------------------------------------------