I've put my coop automation source code on GitHub along with my Arduino Aquarium source code to help others with their projects. The source on git is shown below. Also, here's a zip of the libraries used in this project, as I've been told some are not easy to find these days! CoopControllerLibraries.zip
Here's a breakdown of current features for the Coop:
Here's a breakdown of current features for the Coop:
- Calculate daily sunrise/sunset based on latitude and longitude via the TimeLord library to adjust relative timers
- Close the coop door after sunset *
- Open the coop door at sunrise
- Fill water bowl after sunrise *
- Turn the lights on in the coop before sunset *
- Turn the lights off in the coop awhile after sunset (simulating a 16 hour day, 8 hours darkness encourages laying per Raising Chickens For Dummies
) *
- Feed morning, mid-day, afternoon, and evening *
- Turn on the fan if it gets too hot in the coop, and turn off when temperature drops
- Manage the door motor (if open stop the motor, if closed stop the motor, etc.)
- Fill up the water bowl if the level drops (for thirsty birds)
- Handle input from controller box (buttons/switches)
- Handle input from Infrared receiver
Arduino-Coop GitHub Project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <IRremote.h> | |
#include <IRremoteInt.h> | |
#include <Wire.h> | |
#include <OneWire.h> | |
#include <TimeLord.h> | |
#include <Time.h> | |
#include <TimeAlarms.h> | |
#include <DS1307RTC.h> | |
/** | |
* Copyright 2012, Roger Reed | |
* | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation; either version 2 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
* 02110-1301, USA. | |
*/ | |
// Whether to print debug messages to Serial | |
const boolean SERIAL_DEBUG = false; | |
// Pin definitions | |
const byte LIGHT_TOGGLE_BUTTON_PIN = 2, | |
WATER_BUTTON_PIN = 3, | |
DOOR_RELAY_CLOSE_PIN = 4, | |
DOOR_RELAY_OPEN_PIN = 5, | |
AIR_TEMP_SENSOR_PIN = 6, | |
IR_RECV_PIN = 7, | |
LIGHT_RELAY_PIN = 8, | |
FEEDER_RELAY_PIN = 9, | |
FAN_RELAY_PIN = 10, | |
WATER_SOLENOID_RELAY_PIN = 11, | |
DOOR_OPEN_SWITCH_PIN = A0, | |
DOOR_CLOSE_SWITCH_PIN = A1, | |
FEED_BUTTON_PIN = A2, | |
WATER_FLOAT_SWITCH_PIN = A3, | |
DOOR_CLOSE_STOP_PIN = A6, | |
DOOR_OPEN_STOP_PIN = A7; | |
// Infrared constants | |
const int | |
SONY_IR_0 = 0x910, | |
SONY_IR_1 = 0x10, | |
SONY_IR_2 = 0x810, | |
SONY_IR_3 = 0x410, | |
SONY_IR_4 = 0xC10, | |
SONY_IR_5 = 0x210, | |
SONY_IR_6 = 0xA10, | |
SONY_IR_7 = 0x610, | |
SONY_IR_8 = 0xE10, | |
SONY_IR_9 = 0x110; | |
// Delay constants | |
const int POST_IR_HANDLE_DELAY_MS = 1250, | |
POST_BUTTON_HANDLE_DELAY_MS = 750; | |
// Duration constants | |
const int FEED_DURATION_SEC = 20, | |
WATER_DURATION_SEC = 20; | |
// Time constants | |
const int DOOR_SHUT_AFTER_SUNSET_MIN = 30, | |
LIGHT_PER_DAY_HRS = 16, | |
WATER_AFTER_SUNRISE_MIN = 15, | |
FEED_MORNING_AFTER_SUNRISE_MIN = 30, | |
FEED_AFTERNOON_BEFORE_SUNSET_HR = 4, | |
FEED_EVENING_BEFORE_SUNSET_HR = 1, | |
FEED_MID_DAY_HR = 12, | |
FEED_MID_DAY_MIN = 0, | |
LIGHT_ON_BEFORE_SUNSET_HRS = 2; | |
// Temperature constants | |
const float MIN_REASONABLE_AIR_TEMP_F = 0.0f, // low air temp we should never see; if we do most likely a temp probe issue | |
MAX_REASONABLE_AIR_TEMP_F = 150.0f, // high air temp we should never see | |
INIT_TEMP = -1000.0f, | |
FAN_ON_TEMP_F = 77.0f, // temp fan is turned on | |
FAN_OFF_TEMP_F = 70.0f; // temp fan is turned off; | |
// Location constants | |
const float LATITUDE = 33.0369867, | |
LONGITUDE = -117.2919818; | |
const int TIMEZONE = -8; | |
// Infrared variables | |
IRrecv irRecv(IR_RECV_PIN); | |
decode_results irResults; | |
// Temperature variables | |
OneWire airTempOneWire(AIR_TEMP_SENSOR_PIN); | |
boolean airTempError = false; | |
float airTemp = INIT_TEMP; | |
// Time variables | |
TimeLord coopTimeLord; | |
long feedOnTimeSec = 0, | |
waterOnTimeSec = 0, | |
lastLoopSec = now(); | |
byte openDoorHr, openDoorMin, closeDoorHr, closeDoorMin, | |
lightOnHr, lightOnMin, lightOffHr, lightOffMin, | |
waterHr, waterMin, feedMorningHr, feedMorningMin, | |
feedAfternoonHr, feedAfternoonMin, | |
feedEveningHr, feedEveningMin; | |
// Control variables | |
boolean manualWater = false; | |
void setup() { | |
if(SERIAL_DEBUG){ | |
Serial.begin(57600); // initialize hardware serial port | |
} | |
Wire.begin(); | |
irRecv.enableIRIn(); | |
setSyncProvider(RTC.get); | |
coopTimeLord.TimeZone(TIMEZONE * 60); | |
coopTimeLord.Position(LATITUDE, LONGITUDE); | |
setupOutputPins(); | |
initState(); | |
scheduleDailyAlarms(); | |
scheduleTodayAlarms(); | |
} | |
void loop(){ | |
if (SERIAL_DEBUG) { | |
serialDebugState(); | |
} | |
handleDoor(); | |
updateAirTemp(); | |
handleFan(); | |
handleDurations(); | |
handleControls(); | |
handleFloat(); | |
handleInfrared(); | |
Alarm.delay(0); | |
lastLoopSec = now(); | |
} | |
/** | |
* Calculates alarm times used for scheduling and init state. | |
*/ | |
void calculateAlarmTimes(){ | |
int nowHour = hour(), | |
nowMinute = minute(), | |
nowDay = day(), | |
nowMonth = month(), | |
nowYear = year(); | |
Serial.print("nowHour: "); | |
Serial.println(nowHour); | |
Serial.print("nowMinute: "); | |
Serial.println(nowMinute); | |
Serial.print("nowDay: "); | |
Serial.println(nowDay); | |
Serial.print("nowMonth: "); | |
Serial.println(nowMonth); | |
Serial.print("nowYear: "); | |
Serial.println(nowYear); | |
byte timeLordSunRise[] = {0, 0, 0, nowDay, nowMonth, nowYear}; | |
byte timeLordSunSet[] = {0, 0, 0, nowDay, nowMonth, nowYear}; | |
coopTimeLord.SunRise(timeLordSunRise); | |
coopTimeLord.SunSet(timeLordSunSet); | |
if(SERIAL_DEBUG){ | |
Serial.print("sunrise: "); | |
Serial.print(timeLordSunRise[2]); | |
Serial.print(":"); | |
Serial.println(timeLordSunRise[1]); | |
Serial.print("sunset: "); | |
Serial.print(timeLordSunSet[2]); | |
Serial.print(":"); | |
Serial.println(timeLordSunSet[1]); | |
} | |
openDoorHr = timeLordSunRise[2]; | |
openDoorMin = timeLordSunRise[1]; | |
closeDoorHr = timeLordSunSet[2]; | |
closeDoorMin = timeLordSunSet[1]; | |
if(closeDoorMin + DOOR_SHUT_AFTER_SUNSET_MIN >= 60){ | |
closeDoorHr += (closeDoorMin + DOOR_SHUT_AFTER_SUNSET_MIN) / 60; | |
closeDoorMin = (closeDoorMin + DOOR_SHUT_AFTER_SUNSET_MIN) % 60; | |
}else{ | |
closeDoorMin += DOOR_SHUT_AFTER_SUNSET_MIN; | |
} | |
if(SERIAL_DEBUG){ | |
Serial.print("open door: "); | |
Serial.print(openDoorHr); | |
Serial.print(":"); | |
Serial.println(openDoorMin); | |
Serial.print("close door: "); | |
Serial.print(closeDoorHr); | |
Serial.print(":"); | |
Serial.println(closeDoorMin); | |
} | |
float naturalDaylightHr; | |
naturalDaylightHr = 12.0f-(timeLordSunRise[2] + timeLordSunRise[1]/60.0f); | |
naturalDaylightHr += (timeLordSunSet[2] + timeLordSunSet[1]/60.0f)-12.0f; | |
if(SERIAL_DEBUG){ | |
Serial.print("natural daylight (hrs): "); | |
Serial.println(naturalDaylightHr); | |
} | |
lightOnHr = timeLordSunSet[2]; | |
lightOnMin = timeLordSunSet[1]; | |
lightOffHr = timeLordSunSet[2]; | |
lightOffMin = timeLordSunSet[1]; | |
float lightNeededHrs = LIGHT_PER_DAY_HRS - naturalDaylightHr; | |
int lightNeededMins = lightNeededHrs * 60; | |
Serial.print("light needed (mins): "); | |
Serial.println(lightNeededMins); | |
lightOnHr -= LIGHT_ON_BEFORE_SUNSET_HRS; | |
if(lightOffMin + lightNeededMins >= 60){ | |
lightOffHr += (lightOffMin + lightNeededMins) / 60; | |
lightOffMin = (lightOffMin + lightNeededMins) % 60; | |
}else{ | |
lightOffMin += lightNeededMins; | |
} | |
if(SERIAL_DEBUG){ | |
Serial.print("light on: "); | |
Serial.print(lightOnHr); | |
Serial.print(":"); | |
Serial.println(lightOnMin); | |
Serial.print("light off: "); | |
Serial.print(lightOffHr); | |
Serial.print(":"); | |
Serial.println(lightOffMin); | |
} | |
waterHr = timeLordSunRise[2]; | |
waterMin = timeLordSunRise[1]; | |
feedMorningHr = timeLordSunRise[2]; | |
feedMorningMin = timeLordSunRise[1]; | |
feedAfternoonHr = timeLordSunSet[2]; | |
feedAfternoonMin = timeLordSunSet[1]; | |
feedEveningHr = timeLordSunSet[2]; | |
feedEveningMin = timeLordSunSet[1]; | |
if(waterMin + WATER_AFTER_SUNRISE_MIN >= 60){ | |
waterHr += (waterMin + WATER_AFTER_SUNRISE_MIN) / 60; | |
waterMin = (waterMin + WATER_AFTER_SUNRISE_MIN) % 60; | |
}else{ | |
waterMin += WATER_AFTER_SUNRISE_MIN; | |
} | |
if(feedMorningMin + FEED_MORNING_AFTER_SUNRISE_MIN >= 60){ | |
feedMorningHr += (feedMorningMin + FEED_MORNING_AFTER_SUNRISE_MIN) / 60; | |
feedMorningMin = (feedMorningMin + FEED_MORNING_AFTER_SUNRISE_MIN) % 60; | |
}else{ | |
feedMorningMin += FEED_MORNING_AFTER_SUNRISE_MIN; | |
} | |
feedAfternoonHr -= FEED_AFTERNOON_BEFORE_SUNSET_HR; | |
feedEveningHr -= FEED_EVENING_BEFORE_SUNSET_HR; | |
if(SERIAL_DEBUG){ | |
Serial.print("water: "); | |
Serial.print(waterHr); | |
Serial.print(":"); | |
Serial.println(waterMin); | |
Serial.print("feed (morning): "); | |
Serial.print(feedMorningHr); | |
Serial.print(":"); | |
Serial.println(feedMorningMin); | |
Serial.print("feed (afternoon): "); | |
Serial.print(feedAfternoonHr); | |
Serial.print(":"); | |
Serial.println(feedAfternoonMin); | |
Serial.print("feed (evening): "); | |
Serial.print(feedEveningHr); | |
Serial.print(":"); | |
Serial.println(feedEveningMin); | |
} | |
} | |
/** | |
* Schedule daily alarms. | |
*/ | |
void scheduleDailyAlarms(){ | |
Alarm.alarmRepeat(1, 0, 0, scheduleTodayAlarms); | |
Alarm.alarmRepeat(FEED_MID_DAY_HR, FEED_MID_DAY_MIN, 0, feed); | |
} | |
/** | |
* Schedule today alarms. | |
*/ | |
void scheduleTodayAlarms(){ | |
calculateAlarmTimes(); | |
int nowHr = hourMinuteToHour(hour(), minute()); | |
if(nowHr < hourMinuteToHour(openDoorHr, openDoorMin)){ | |
Alarm.alarmOnce(openDoorHr, openDoorMin, 0, enableDoorOpening); | |
} | |
if(nowHr < hourMinuteToHour(closeDoorHr, closeDoorMin)){ | |
Alarm.alarmOnce(closeDoorHr, closeDoorMin, 0, enableDoorClosing); | |
} | |
if(nowHr < hourMinuteToHour(lightOnHr, lightOnMin)){ | |
Alarm.alarmOnce(lightOnHr, lightOnMin, 0, lightOn); | |
} | |
if(nowHr < hourMinuteToHour(lightOffHr, lightOffMin)){ | |
Alarm.alarmOnce(lightOffHr, lightOffMin, 0, lightOff); | |
} | |
if(nowHr < hourMinuteToHour(waterHr, waterMin)){ | |
Alarm.alarmOnce(waterHr, waterMin, 0, water); | |
} | |
if(nowHr < hourMinuteToHour(feedMorningHr, feedMorningMin)){ | |
Alarm.alarmOnce(feedMorningHr, feedMorningMin, 0, feed); | |
} | |
// mid day feed scheduled as daily at noon in scheduleDailyAlarms method | |
if(nowHr < hourMinuteToHour(feedAfternoonHr, feedAfternoonMin)){ | |
Alarm.alarmOnce(feedAfternoonHr, feedAfternoonMin, 0, feed); | |
} | |
if(nowHr < hourMinuteToHour(feedEveningHr, feedEveningMin)){ | |
Alarm.alarmOnce(feedEveningHr, feedEveningMin, 0, feed); | |
} | |
} | |
/** | |
* Initialize state based on current time | |
*/ | |
void initState(){ | |
fanOff(); | |
feedOff(); | |
waterOff(); | |
calculateAlarmTimes(); | |
int nowHr = hourMinuteToHour(hour(), minute()); | |
if(nowHr > hourMinuteToHour(lightOnHr, lightOnMin) && nowHr < hourMinuteToHour(lightOffHr, lightOffMin)){ | |
Serial.println("light init on"); | |
lightOn(); | |
}else{ | |
Serial.println("light init off"); | |
lightOff(); | |
} | |
if(nowHr > hourMinuteToHour(openDoorHr, openDoorMin) && nowHr < hourMinuteToHour(closeDoorHr, closeDoorMin)){ | |
Serial.println("door init open"); | |
enableDoorOpening(); | |
}else{ | |
Serial.println("door init close"); | |
enableDoorClosing(); | |
} | |
} | |
/** | |
* Set pin modes | |
*/ | |
void setupOutputPins(){ | |
// relays | |
pinMode(DOOR_RELAY_OPEN_PIN, OUTPUT); | |
pinMode(DOOR_RELAY_CLOSE_PIN, OUTPUT); | |
pinMode(FEEDER_RELAY_PIN, OUTPUT); | |
pinMode(LIGHT_RELAY_PIN, OUTPUT); | |
pinMode(FAN_RELAY_PIN, OUTPUT); | |
pinMode(WATER_SOLENOID_RELAY_PIN, OUTPUT); | |
} | |
void serialDebugState(){ | |
// Serial.print("LIGHT_TOGGLE_BUTTON_PIN ("); | |
// Serial.print(LIGHT_TOGGLE_BUTTON_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(LIGHT_TOGGLE_BUTTON_PIN)); | |
// Serial.print(", WATER_BUTTON_PIN ("); | |
// Serial.print(WATER_BUTTON_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(WATER_BUTTON_PIN)); | |
// Serial.print(", DOOR_OPEN_SWITCH_PIN ("); | |
// Serial.print(DOOR_OPEN_SWITCH_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(DOOR_OPEN_SWITCH_PIN)); | |
// Serial.print(", DOOR_CLOSE_SWITCH_PIN ("); | |
// Serial.print(DOOR_CLOSE_SWITCH_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(DOOR_CLOSE_SWITCH_PIN)); | |
// Serial.print(", WATER_FLOAT_SWITCH_PIN ("); | |
// Serial.print(WATER_FLOAT_SWITCH_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(WATER_FLOAT_SWITCH_PIN)); | |
// Serial.print(", FEED_BUTTON_PIN ("); | |
// Serial.print(FEED_BUTTON_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(digitalRead(FEED_BUTTON_PIN)); | |
// Serial.print(", DOOR_CLOSE_STOP_PIN ("); | |
// Serial.print(DOOR_CLOSE_STOP_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(analogRead(DOOR_CLOSE_STOP_PIN) > 500); | |
// Serial.print(", DOOR_OPEN_STOP_PIN ("); | |
// Serial.print(DOOR_OPEN_STOP_PIN, DEC); | |
// Serial.print("): "); | |
// Serial.print(analogRead(DOOR_OPEN_STOP_PIN) > 500); | |
// Serial.println(); | |
Serial.print("airTemp: "); | |
if(!airTempError){ | |
Serial.print(airTemp); | |
Serial.println(); | |
}else{ | |
Serial.println("ERROR"); | |
} | |
Serial.print("time: "); | |
Serial.print(year(), DEC); | |
Serial.print('/'); | |
Serial.print(month(), DEC); | |
Serial.print('/'); | |
Serial.print(day(), DEC); | |
Serial.print(' '); | |
Serial.print(hour(), DEC); | |
Serial.print(':'); | |
Serial.print(minute(), DEC); | |
Serial.print(':'); | |
Serial.print(second(), DEC); | |
Serial.println(); | |
} | |
/** | |
* Handles any action that needs to be taken onfloat switch. | |
*/ | |
void handleFloat(){ | |
if(digitalRead(WATER_FLOAT_SWITCH_PIN)){ | |
waterOn(); | |
}else if(!manualWater){ | |
waterOff(); | |
} | |
} | |
/** | |
* Handles any action that needs to be taken on control buttons or switches. | |
*/ | |
void handleControls(){ | |
if(isLightToggleButton()){ | |
Serial.println("toggle light button"); | |
toggleLight(); | |
delay(POST_BUTTON_HANDLE_DELAY_MS); | |
} | |
if(isFeedButton()){ | |
Serial.println("feed button"); | |
feed(); | |
delay(POST_BUTTON_HANDLE_DELAY_MS); | |
} | |
if(isWaterButton()){ | |
Serial.println("water button"); | |
manualWater = true; | |
water(); | |
delay(POST_BUTTON_HANDLE_DELAY_MS); | |
} | |
if(isDoorOpenSwitch()){ | |
Serial.println("door open switch"); | |
enableDoorOpening(); | |
} | |
if(isDoorCloseSwitch()){ | |
Serial.println("door close switch"); | |
enableDoorClosing(); | |
} | |
} | |
/** | |
* Handles any action that needs to be taken on the fan. | |
*/ | |
void handleFan() { | |
if(!airTempError){ | |
if(airTemp >= FAN_ON_TEMP_F){ | |
fanOn(); | |
} | |
if(airTemp <= FAN_OFF_TEMP_F){ | |
fanOff(); | |
} | |
} | |
} | |
/** | |
* Updates air temp and checks for probe error. | |
*/ | |
void updateAirTemp() { | |
airTempError = !updateTemp(&airTempOneWire, &airTemp, airTempError); | |
if(airTempError || airTemp <= MIN_REASONABLE_AIR_TEMP_F || airTemp >= MAX_REASONABLE_AIR_TEMP_F){ | |
airTempError = true; | |
} | |
} | |
/** | |
* Handles any action that needs to be taken on the door motor. | |
*/ | |
void handleDoor() { | |
if(isDoorClosing() && isDoorCloseStop()){ | |
disableDoor(); | |
} | |
if(isDoorOpening() && isDoorOpenStop()){ | |
disableDoor(); | |
} | |
} | |
/** | |
* Handles any durations (e.g. feed, water) that need to be monitored and turned off after set time. | |
*/ | |
void handleDurations(){ | |
if(isFeedOn()){ | |
feedOnTimeSec += now() - lastLoopSec; | |
if(SERIAL_DEBUG){ | |
Serial.print("feeding ("); | |
Serial.print(feedOnTimeSec); | |
Serial.println(" sec)"); | |
} | |
if(feedOnTimeSec >= FEED_DURATION_SEC){ | |
feedOff(); | |
} | |
} | |
if(isWaterOn()){ | |
waterOnTimeSec += now() - lastLoopSec; | |
if(SERIAL_DEBUG){ | |
Serial.print("watering ("); | |
Serial.print(waterOnTimeSec); | |
Serial.println(" sec)"); | |
} | |
if(waterOnTimeSec >= WATER_DURATION_SEC){ | |
waterOff(); | |
manualWater = false; | |
} | |
} | |
} | |
/** | |
* Handles any action that need to be taken from Infrared | |
*/ | |
void handleInfrared() { | |
if (!irRecv.decode(&irResults)) { | |
irRecv.resume(); | |
return; | |
} | |
if(SERIAL_DEBUG){ | |
Serial.print("irResults.value: "); | |
Serial.println(irResults.value, HEX); | |
} | |
switch(irResults.value){ | |
case SONY_IR_4: | |
// water | |
Serial.println("water remote"); | |
manualWater = true; | |
water(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
case SONY_IR_5: | |
// toggle light | |
Serial.println("toggle light remote"); | |
toggleLight(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
case SONY_IR_6: | |
// feed | |
Serial.println("feed remote"); | |
feed(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
case SONY_IR_7: | |
// open door | |
Serial.println("door open remote"); | |
enableDoorOpening(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
case SONY_IR_8: | |
// close door | |
Serial.println("door close remote"); | |
enableDoorClosing(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
case SONY_IR_9: | |
// toggle fan | |
Serial.println("toggle fan remote"); | |
toggleFan(); | |
delay(POST_IR_HANDLE_DELAY_MS); | |
break; | |
} | |
irRecv.resume(); | |
} | |
// feed methods | |
void feed(){ | |
feedOnTimeSec = 0; | |
feedOn(); | |
} | |
void feedOn(){ | |
digitalWrite(FEEDER_RELAY_PIN, LOW); | |
} | |
void feedOff(){ | |
digitalWrite(FEEDER_RELAY_PIN, HIGH); | |
} | |
boolean isFeedOn(){ | |
return digitalRead(FEEDER_RELAY_PIN) == LOW; | |
} | |
boolean isFeedButton(){ | |
return digitalRead(FEED_BUTTON_PIN) == HIGH; | |
} | |
// end feed methods | |
// water methods | |
void water(){ | |
waterOnTimeSec = 0; | |
waterOn(); | |
} | |
void waterOn(){ | |
digitalWrite(WATER_SOLENOID_RELAY_PIN, HIGH); | |
} | |
void waterOff(){ | |
digitalWrite(WATER_SOLENOID_RELAY_PIN, LOW); | |
} | |
boolean isWaterOn(){ | |
return digitalRead(WATER_SOLENOID_RELAY_PIN) == HIGH; | |
} | |
boolean isWaterButton(){ | |
return digitalRead(WATER_BUTTON_PIN) == HIGH; | |
} | |
// end feed methods | |
// light methods | |
void toggleLight(){ | |
if(isLightOn()){ | |
lightOff(); | |
}else{ | |
lightOn(); | |
} | |
} | |
boolean isLightOn(){ | |
return digitalRead(LIGHT_RELAY_PIN) == LOW; | |
} | |
void lightOn(){ | |
digitalWrite(LIGHT_RELAY_PIN, LOW); | |
} | |
void lightOff(){ | |
digitalWrite(LIGHT_RELAY_PIN, HIGH); | |
} | |
boolean isLightToggleButton(){ | |
return digitalRead(LIGHT_TOGGLE_BUTTON_PIN) == HIGH; | |
} | |
// end light methods | |
// fan methods | |
void toggleFan(){ | |
if(isFanOn()){ | |
fanOff(); | |
}else{ | |
fanOn(); | |
} | |
} | |
boolean isFanOn(){ | |
return digitalRead(FAN_RELAY_PIN) == LOW; | |
} | |
void fanOn(){ | |
digitalWrite(FAN_RELAY_PIN, LOW); | |
} | |
void fanOff(){ | |
digitalWrite(FAN_RELAY_PIN, HIGH); | |
} | |
// end fan methods | |
// door methods | |
boolean isDoorOpenSwitch(){ | |
return digitalRead(DOOR_OPEN_SWITCH_PIN); | |
} | |
boolean isDoorCloseSwitch(){ | |
return digitalRead(DOOR_CLOSE_SWITCH_PIN); | |
} | |
boolean isDoorClosing(){ | |
return digitalRead(DOOR_RELAY_CLOSE_PIN) == HIGH; | |
} | |
boolean isDoorOpening(){ | |
return digitalRead(DOOR_RELAY_OPEN_PIN) == HIGH; | |
} | |
void enableDoorClosing(){ | |
if(!isDoorCloseStop()){ | |
digitalWrite(DOOR_RELAY_OPEN_PIN, LOW); | |
digitalWrite(DOOR_RELAY_CLOSE_PIN, HIGH); | |
} | |
} | |
void enableDoorOpening(){ | |
if(!isDoorOpenStop()){ | |
digitalWrite(DOOR_RELAY_CLOSE_PIN, LOW); | |
digitalWrite(DOOR_RELAY_OPEN_PIN, HIGH); | |
} | |
} | |
boolean isDoorInMiddle(){ | |
return !isDoorOpenStop() && !isDoorCloseStop(); | |
} | |
boolean isDoorCloseStop(){ | |
return analogRead(DOOR_CLOSE_STOP_PIN) > 500; | |
} | |
boolean isDoorOpenStop(){ | |
return analogRead(DOOR_OPEN_STOP_PIN) > 500; | |
} | |
void disableDoor(){ | |
digitalWrite(DOOR_RELAY_OPEN_PIN, LOW); | |
digitalWrite(DOOR_RELAY_CLOSE_PIN, LOW); | |
} | |
// end door methods | |
/** | |
* Updates a single temp | |
*/ | |
boolean updateTemp(OneWire * oneWireTherm, float * fltTemp, boolean previousError){ | |
byte thermAddr[8], data[12]; | |
oneWireTherm->reset_search(); | |
oneWireTherm->search(thermAddr); | |
if(OneWire::crc8(thermAddr,7)!=thermAddr[7]) { // checksum invalid | |
return false; | |
} | |
if(!oneWireTherm->reset()){ | |
return false; | |
} | |
oneWireTherm->select(thermAddr); | |
oneWireTherm->write(0x44, 1); // start conversation w/ parasite power | |
if(previousError){ | |
delay(1000); | |
} | |
if(!oneWireTherm->reset()){ | |
return false; | |
} | |
oneWireTherm->select(thermAddr); | |
oneWireTherm->write(0xBE); // read scratchpad | |
for(int i=0;i<9;i++) { // need 9 bytes | |
data[i] = oneWireTherm->read(); | |
} | |
*fltTemp = getTemperature(data[0], data[1]); | |
return true; | |
} | |
/** | |
* Converts celsius to fahrenheit | |
*/ | |
float convertCeliusToFahrenheit(float c) { | |
return((c*1.8)+32); | |
} | |
/** | |
* Converts fahrenheit to celsius | |
*/ | |
float convertFahrenheitToCelius(float f) { | |
return((f-32)*0.555555556); | |
} | |
float hourMinuteToHour(int hour, int minute){ | |
return hour + minute/60.0f; | |
} | |
/** | |
* Gets temp from bytes for OneWire | |
*/ | |
float getTemperature(int lowByte, int highByte) { | |
int intPostDecimal, boolSign, intHexTempReading, intTempReadingBeforeSplit, preDecimal, i; | |
float fltPostDecimal, fltTemp; | |
intHexTempReading = (highByte << 8) + lowByte; | |
boolSign = intHexTempReading & 0x8000; | |
if(boolSign) { | |
intHexTempReading = (intHexTempReading ^ 0xffff) + 1; | |
} | |
intTempReadingBeforeSplit = 6.25 * intHexTempReading; // multiply by (100 * precision) = (100 * 0.0625) = 6.25 = 12-bit precision | |
preDecimal = intTempReadingBeforeSplit / 100; | |
intPostDecimal = intTempReadingBeforeSplit % 100; | |
fltPostDecimal = intPostDecimal; | |
if(intPostDecimal<10) { | |
fltTemp = preDecimal+(fltPostDecimal/1000); | |
} | |
else { | |
fltTemp = preDecimal+(fltPostDecimal/100); | |
} | |
if(boolSign) { | |
fltTemp = -fltTemp; | |
} | |
return convertCeliusToFahrenheit(fltTemp); | |
} |
Hello,
ReplyDeleteI just wanted to thank you for posting your code. The comments and notes you left are what allowed me to finally get the TimeLord.h library to work.
Thanks
hi ,
ReplyDeletei m sorry for my english (From France).
First, thanks for your project and sharring it !
I m doing a coop almost like you , only door automated with rtc.
When i m compilling the code , it give me an error
"PAAG.ino: In function 'void scheduleTodayAlarms()':
PAAG:99: error: 'nowhour' was not declared in this scope
PAAG:99: error: 'nowMinute' was not declared in this scope
PAAG:99: error: 'hourMinuteToHour' was not declared in this scope"
i tried your other program"aquarium", and it worked well.
thanks for your help
float hourMinuteToHour(int hour, int minute){
Deletereturn hour + minute/60.0f;
}
Late reply, but hope you've got it working now. Could be a library or version issue as I'm sure is successfully compiled on my end before I posted the code.
DeleteThis comment has been removed by the author.
ReplyDeleteThank you... thank you! We are novice Arduino users and
ReplyDeleteyour chicken coop sketch is very much like the code we're trying to develop for our school garden's aquaponics greenhouse. We needed sunrise and sunset plus the monitoring and control of various devices, much like your chicken coop. About the only thing we'll still need to develop is linking the sun's altitude during the day to PWM high intensity LED lamps... I'm pretty sure the timelord library has the capability to do this for us... any hints?
I'm not familiar with that feature of the library. You could always simplify and set a timer that started a gradual increase in PWM signal until full brightness over time on the main loop. And then another timer to start a gradual decrease in full brightness. I guess it depends how precise you're trying to get. Good luck!
ReplyDeleteI'll try for a better understanding of TimeLord. Thanks for the workaround idea.
ReplyDeleteDownloaded the code and correct libraries. Upon compiling I get the following errors. Is this a problem with/between the IRremote and IRremoteInt libraries. Any ideas?:
IRremote\IRremote.cpp.o: In function `MATCH(int, int)':
/IRremoteInt.h:180: multiple definition of `MATCH(int, int)'
chickencoopcontroller_ino.cpp.o:C:\Users\emorse\Documents\Arduino\libraries\IRremote/IRremoteInt.h:180: first defined here
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
IRremote\IRremote.cpp.o: In function `MATCH_MARK(int, int)':
/IRremoteInt.h:181: multiple definition of `MATCH_MARK(int, int)'
chickencoopcontroller_ino.cpp.o:C:\Users\emorse\Documents\Arduino\libraries\IRremote/IRremoteInt.h:181: first defined here
IRremote\IRremote.cpp.o: In function `MATCH_SPACE(int, int)':
/IRremoteInt.h:182: multiple definition of `MATCH_SPACE(int, int)'
chickencoopcontroller_ino.cpp.o:C:\Users\emorse\Documents\Arduino\libraries\IRremote/IRremoteInt.h:182: first defined here
Forgot to mention that we are using an Arduino Mega. The Arduino Forum notes problems using the IRremote library with the Mega.
ReplyDeletesee http://forum.arduino.cc/index.php?PHPSESSID=cee6qp7uhk3b4cobegmggpoer2&action=printpage;topic=28434.0
The Mega uses pin 9 rather than pin 3 to connect to the ATmega chip OCR2B. Rather than change the coop code, the forum suggests "a minor change in the library." "you should modify the enableIROut() library function to initialize Pin 9, not Pin 3."
Since many folks would prefer the Mega for extra flexibility, would you: 1.recommend modifying the coop code or the library code. 2. Are changes also required in the IRremoteInt.h library?
Mr Reed do you still visit this blog if you do can you post up your libraries for your aquarium build and the coop some of them are not available or well hidden
ReplyDeleteThank you
Sorry for the delay folks! I have uploaded the libraries for both the Coop and Aquarium projects as a zip file on github. Also linked to from these blogs. Enjoy!
ReplyDeleteI found a "bug". on line 315 and 362 you call "int nowHr = hourMinuteToHour(hour(), minute());" But hourMinuteToHour() returns a float not an int.
ReplyDeleteThis can cause calc issues at certain times.
What is calling the scheduleTodayAlarms() function in the loop? how do the alarm once alarms in the function actually make the function work? I am using some of this code and it initializes just fine, but none of the alarms that are being scheduled are actually causing functions to operate.
ReplyDeleteThis comment has been removed by the author.
ReplyDeletegreat work u have done here
ReplyDeleteI am having a hard time converting to Celsius any suggestions?
Hi i have an issues with the airtemp. When i Start the manuel watering i have a airtemp error and i can't toggle the light on and off. do you have any ideas?
ReplyDeleteneed to add timelib.h to get it working on my setup. if your getting alot errors like now() etc, try that.
ReplyDeleteI want to use the RTC PCF8563. What do I need to change in the code to get it to work. I added the library but get error when compiling - 'RTC' was not declared in this scope - With this line - setSyncProvider(RTC.get);
ReplyDeleteI deleted that line which allows it to compile but the time resets to 1970 every time the serial monitor opens and doesn't hold the time that was set from the IDE examples.
Delete