Interrupts and wdt code – Attiny85

I use that code as a basis for all my sensor sketch developments with a Attiny85 chip.
It might be useful for some geeks out there.

attiny85Actually, this page is a kind of reminder and a startup for all my new dev.
It’s based on the use of watchdog timers and interrupts which is a very simple way to code sensors (temperature, humidity, shock, etc) and very efficient for not wasting power for the circuit. A Attiny85 should draw only a few µA when sleeping.

//
// https://blog.onlinux.fr
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+

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


#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
//
// W H Y  V O L A T I L E ?
//
// Why volatile?
// ---->
// https://www.arduino.cc/en/Reference/Volatile
// volatile keyword:
// Specifically, it directs the compiler to load the variable from RAM and not from a storage register,
// which is a temporary memory location where program variables are stored and manipulated. 
// Under certain conditions, the value for a variable stored in registers can be inaccurate.

volatile boolean f_wdt = 1;

// Use pin PB0 as wake up pin
const int wakeUpPin = 0;

const byte pinLed = 4;
const byte pinLedRed = 3;
  
void blink(int ii) {
  pinMode(pinLed, OUTPUT);
  for (byte i = ii ;  i > 0 ; i--){
     digitalWrite(pinLed, HIGH);
     delay(50);
     digitalWrite(pinLed, LOW); 
     delay(50);
  }
  
  pinMode(pinLed, INPUT); // reduce power
}

void blinkRed(int ii) {
  pinMode(pinLedRed, OUTPUT);
  for (byte i = ii ;  i > 0 ; i--){
     digitalWrite(pinLedRed, HIGH);
     delay(50);
     digitalWrite(pinLedRed, LOW); 
     delay(50);
  }
  pinMode(pinLedRed, INPUT); // reduce power
}

/******************************************************************/
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
  ww=bb;

  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
} 

void system_sleep() {
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  sleep_mode();                        // System sleeps here, waiting for interrupt

  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON
}

// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
  f_wdt=1;  // set global flag
}


void setup()
{
  setup_watchdog(9);
  pinMode(wakeUpPin, INPUT);        // Set the pin to input
  digitalWrite(wakeUpPin, HIGH);    // Activate internal pullup resistor 
  PCMSK  |= bit (PCINT0);  //                                   Pin Change Mask Register
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts  General Interrupt Flag Register
  GIMSK  |= bit (PCIE);    // enable pin change interrupts      General Interrupt Mask Register
  sei();                   // enable interrupts
}

ISR (PCINT0_vect){}

void loop()
{
  system_sleep();
  if ( f_wdt > 0) {  // watchdog signal
    blink(5);
    f_wdt=0;         //reset flag
  } else {
    blinkRed(5);     // PCINT0 occured
  }
    
}