Newer
Older
chicken_door / chicken_door.ino
/*
 * Open and close the door on the chicken house depending on the
 * ambient light levels.
 * Copyright Alex Tucker 2013
 *
 */


// Enable deep sleep with wake on watchdog interrupt.
// Based on Donal Morrissey's code at http://donalmorrissey.blogspot.co.uk/2010/04/sleeping-arduino-part-5-wake-up-via.html
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

volatile int f_wdt=1;

ISR(WDT_vect) {
  if (f_wdt == 0)
  {
    f_wdt=1;
  }
}

void enterSleep(void) {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   /* PWR_SAVE or PWR_DOWN */
  sleep_enable();  
  sleep_mode();
  
  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */
  
  /* Re-enable the peripherals. */
  power_all_enable();
//  delay(8000);
//  f_wdt = 1;
}

#define ONOFF_PIN        5
#define FWREV_PIN        3
#define DOOR_CONTACT_PIN 4
#define BUTTON_PIN       2
#define PHOTOCELL_PIN    0

#define INITIALIZE     0
#define WAIT_FOR_LIGHT 1
#define LOWER_DOOR     2
#define WAIT_FOR_DARK  3
#define RAISE_DOOR     4

#define BRIGHTNESS_SIZE  3
#define BRIGHT_THRESHOLD 800
#define OPEN_DELAY       6250

word brightness[BRIGHTNESS_SIZE];
int state = WAIT_FOR_LIGHT;

void setupWDT() {
  /* Clear the reset flag. */
  MCUSR &= ~(1<<WDRF);
  
  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1<<WDCE) | (1<<WDE);

  /* set new watchdog timeout prescaler value */
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */
  
  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
}

void setup() {
  pinMode(ONOFF_PIN, OUTPUT);
  pinMode(FWREV_PIN, OUTPUT);
  pinMode(DOOR_CONTACT_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT);
  digitalWrite(ONOFF_PIN, LOW);
  digitalWrite(FWREV_PIN, LOW);
//  Serial.begin(9600);
  state = INITIALIZE;
  word currentBrightness = analogRead(PHOTOCELL_PIN);
  for (int i = 0; i < BRIGHTNESS_SIZE; i++) {
    brightness[i] = currentBrightness;
  }
  setupWDT();
}

void raiseDoor() {
  digitalWrite(FWREV_PIN, LOW);
  delay(10);
  digitalWrite(ONOFF_PIN, HIGH);
  while (digitalRead(DOOR_CONTACT_PIN) == LOW) {
    delay(100);
  }
  digitalWrite(ONOFF_PIN, LOW);
  delay(10);
}

void lowerDoor() {
  digitalWrite(FWREV_PIN, HIGH);
  delay(10);
  digitalWrite(ONOFF_PIN, HIGH);
  delay(OPEN_DELAY);
  digitalWrite(ONOFF_PIN, LOW);
  delay(10);
  digitalWrite(FWREV_PIN, LOW);
}

word readBrightness() {
  word currentBrightness = analogRead(PHOTOCELL_PIN);
  unsigned long total = brightness[0];
  for (int i = 0; i < BRIGHTNESS_SIZE; i++) {
    if (i == (BRIGHTNESS_SIZE - 1)) {
      brightness[i] = analogRead(PHOTOCELL_PIN);
    } else {
      brightness[i] = brightness[i+1];
    }
    total += brightness[i];
  }
  word avg = total / (BRIGHTNESS_SIZE + 1);
/*  Serial.begin(9600);
  Serial.print("Brightness, avg: ");
  Serial.print(avg);
  Serial.print(", current: ");
  Serial.print(currentBrightness);
  Serial.print("\n"); */
  return avg;
}

void loop() {
  if (f_wdt == 1) {
    f_wdt = 0;
    
    switch (state) {
      case INITIALIZE:
        if (digitalRead(DOOR_CONTACT_PIN) == LOW) {
          raiseDoor();
        }
        state = WAIT_FOR_LIGHT;
        break;
      case WAIT_FOR_LIGHT:
        if (readBrightness() > BRIGHT_THRESHOLD) {
          state = LOWER_DOOR;
        }
        break;
      case LOWER_DOOR:
        lowerDoor();
        state = WAIT_FOR_DARK;
        break;
      case WAIT_FOR_DARK:
        if (readBrightness() < BRIGHT_THRESHOLD) {
          state = RAISE_DOOR;
        }
        break;
      case RAISE_DOOR:
        raiseDoor();
        state = WAIT_FOR_LIGHT;
        break;
    }
    enterSleep();
  }
}