/* 2019-11-10
 * 
 * This script sets up a watchdog and sleep mode to spend most of the time sleeping.
 * When a counter expires, an output is breifly turned on to make a chirp sound,
 * then the micro goes back to sleep.
 * 
 * This is designed for the ATTiny85 but can be adapted to whatever.
 */

#include <avr/sleep.h>
#include <avr/wdt.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

int counter = random(900);
volatile boolean f_wdt = 1;


// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  sleep_mode();         // System sleeps here

  sleep_disable();      // System continues execution here when watchdog timed out
}


// 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);
}


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


void setup() {
  setup_watchdog(9);       // set watchdog to ~8 seconds
  cbi(ADCSRA, ADEN);       // switch ADC off to save power
  
  pinMode(0, OUTPUT);      // set D0 as output
  digitalWrite(0, HIGH);   // turn the buzzer on
  delay(100);              // wait for a long moment
  digitalWrite(0, LOW);    // turn the buzzer off
  delay(100);              // wait for a long moment
  digitalWrite(0, HIGH);   // turn the buzzer on
  delay(15);               // wait for a short moment
  digitalWrite(0, LOW);    // turn the buzzer off
  pinMode(0, INPUT);       // set D0 as input
}


void loop() {
  if (f_wdt == 1) {              // if a watchdog timeout occured
    f_wdt = 0;                   // reset flag
    if (0 > counter--) {         // if it's time to beep
      pinMode(0, OUTPUT);        // set D0 as output
      if (!random(10)) {         // 10% chance of a double beep
        digitalWrite(0, HIGH);   // turn the buzzer on
        delay(15);               // wait for a bit
        digitalWrite(0, LOW);    // turn the buzzer off
        delay(100);
      }
      digitalWrite(0, HIGH);     // turn the buzzer on
      delay(15);                 // wait for a bit
      digitalWrite(0, LOW);      // turn the buzzer off
      pinMode(0, INPUT);         // disable the output
      counter = random(900);     // choose how long to wait till the next beep
    }
    system_sleep();              // sleep
  }
}
