#include <math.h>
#include <Dusk2Dawn.h> // This library seems to take the least memory and does what is needed
#include <Arduino.h>
#include <Wire.h>
#include <RTClib.h> // Library for the RTC (real Time Clock). The project uses a DS1307 to which a Dallas DS18B20 / 1-Wire Digital Thermometer TO-92 was added
// IMPORTANT NOTE: as this project uses both 3.3V (RTC) and 5V (the others) I2C modules. Be sure to add a bi-direction level shifter or you are likely to blow-up your Arduino. I did that twice before getting the message... :-(
#include <RCSwitch.h> // Library for the 433Mhz remote control
#include <U8x8lib.h> // This library seems to take the least memory and does what is needed
#include <OneWire.h> // These two libraries are used to measure the temperature with the DS18B20 soldered onto the RTC
#include <DallasTemperature.h>
/* ---------------------- Some basic settings you will need/want to change to suit your needs ---------------------------------- */
#define myLatitude 43.649572 // My chicken coop location
#define myLongtitude 3.562498
#define myTimezone 0.0 // In case you need to adjust your timezone. See the library documentation for details
int myDoorDuration = 34; // Time to open or close the door. The time duration in seconds we need to provide power to the linear actuator
int closingPause = 3; // Number of minutes the Door Closing will pause when an obstacle is detected inside the door while closing
int sunRiseOffset = 0; // Number of minutes the coop door will open earlier / later than the calculated time
int sunSetOffset = 30; // Number of minutes the coop door will close earlier / later than the calculated time
int enableFlash1 = 1; // Setting to enable Flash1 or not. 1=ON, 0=OFF. Obstacle detector and Flash LED above the door (that closes at night) pointing outwards
int enableFlash2 = 1; // Setting to enable Flash2 or not. 1=ON, 0=OFF. Obstacle detector and Flash LED above the door to the garden pointing outwards (door does not close automatically at night), pointing outwards
int flash1duration = 3; // Duration of the flash light in seconds above the door (that closes at night) pointing outwards
int flash2duration = 3; // Duration of the flash light in seconds above the door to the garden pointing outwards (door does not close automatically at night), pointing outwards
int screenOnTime = 10; // Duration in seconds the screen stays on. Required as there is a conflict between the 'screen on' state of the OLED with the used library and RCSwitch. When the screen is on, the remote control signals are not captured....
int enableRemote = 1; // Setting to use remote control or not. 1=ON, 0=OFF
unsigned long remoteDoorOpen = 1396644; // These 4 rows contain the codes from the 433Mhz remote-control. You will need to use the RCSwitch > ReceiveDemo_Simple from the IDE examples to detect yours and update these codes
unsigned long remoteDoorClose = 1396648;
unsigned long remoteDoorStop = 1396641;
unsigned long remoteLightFlash = 1396642;
#define ONE_WIRE_BUS A3 // Analog pin for the data wire for temperature measuments
/* ---------------------- These settings are only needed to be changed if you also change the electric wiring ------------------ */
const int openRelay = 3; // This project uses a linear actuator and only requires +/- 12V for actioning which is managed by 2 relays
const int closeRelay = 4;
const int obstacleDoor = 5; // IR Obstacle detector inside the door
const int DoorOpenLed = 6; // Lights up when the door is open
const int DoorClosedLed = 7; // Lights up when the door is closed
const int pushOpen = 8; // Open the door
const int pushClose = 9; // Close the door
const int obstacleFlash1 = 10; // IR obstacle detector above the door (that closes at night) pointing outwards
const int obstacleFlash2 = 11; // IR obstacle detector above the door to the garden pointing outwards (door does not close automatically at night), pointing outwards
const int flash1Relay = 12; // Actioning the flash-light door (outside obstacle)
const int flash2Relay = 13; // Actioning the flash-light exterior (exterior obstacle)
int screenSwitch = A2; // Analog pin for the momentary push button to switch-on the OLED
/* ---------------------- End of settings ----------------------------------------------------------------------------------------- */
RTC_DS1307 rtc; // if you are getting a "165" output on the OLED instead of the propoer time & date, you probably need to use a 3.3V pin instead of a 5V pin for your RTC module (and therefore a I2C level shifter)
RCSwitch mySwitch = RCSwitch();
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
Dusk2Dawn myLocation(myLatitude, myLongtitude, myTimezone);
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature chip.
int year, month, day, hour, minute, second, x, mySunrise, mySunset, mySRHR, mySRMN, mySDHR, mySDMN, myDoor, myDoorObstacle, closingDuration, pausedAfter, myNow, myCounter, myTemp;
int doorDetection = HIGH; // no obstacle
int flash1Detection = HIGH; // no obstacle
int flash2Detection = HIGH; // no obstacle
int myDoorStatus = 0;
int myPause = 0;
long startDoorMovement;
long stopDoorMovement;
/*
// myDoorStatus = 0 => default value at startup. Status will show "Initiate". Press the open or close button at startup (depending the actual door status) to change and capture the correct status
// myDoorStatus = 1 => Door open
// myDoorStatus = 2 => Door opening
// myDoorStatus = 3 => Door closed
*/
// Most activity is managed thru functions in order to keep the core code clean and easily update/add functionality
void openDoor();
void closeDoor();
void stopDoor();
void flash1();
void flash2();
void checkRemote();
void getTime();
void getRiseAndSet();
// A number of messages to print on the OLED.
void screenON();
void printMain();
void printDate();
void printTime();
void printSunrise();
void printSunset();
void printInitiate();
void printOpening();
void printOpen();
void printClosing();
void printClosed();
void printPaused();
void printFlash1();
void printFlash2();
void printTemp();
void printCounter();
void resetCounter();
/* ---------------------- Program Setup ------------------ */
void setup() {
Serial.begin(9600);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
Wire.begin();
mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 It can also be (1), that would be pin #3
u8x8.initDisplay();
u8x8.clearDisplay();
u8x8.setPowerSave(0);
u8x8.setFont(u8x8_font_victoriamedium8_r); // This font allows for 7 rows on the OLED 28X64
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Note that if you are having a (charged) battery on your RTC you need to run this command only once. If you let it run each time the code boots, it will reset the date/time to the original loaded when it was connected thru USB when you reboot the board.
sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement
// Before you connect the relay board check the Voltage required to make the relays function correctly. In my case I need to supply it with 12V on the JD-VCC pin and GND (read: don't use GND from the Arduino board in this case).
pinMode(openRelay, OUTPUT); // set pin as output for relay 1
pinMode(closeRelay, OUTPUT); // set pin as output for relay 2
pinMode(DoorOpenLed, OUTPUT); // set pin as output for DoorOpenLed
pinMode(DoorClosedLed, OUTPUT); // set pin as output for DoorClosedLed
pinMode(flash1Relay, OUTPUT); // set pin as output for relay 3
pinMode(flash2Relay, OUTPUT); // set pin as output for relay 4
pinMode(obstacleDoor, INPUT); // set pin as input for the door obstacle infrared detector
pinMode(obstacleFlash1, INPUT); // set pin as input for the IR obstacle detector above the door (that closes at night) pointing outwards
pinMode(obstacleFlash2, INPUT); // set pin as input for the IR obstacle detector above the door to the garden pointing outwards (door does not close at night), pointing outwards
pinMode(pushOpen, INPUT); // set pin as intput for pushbutton to manually open the coop door
pinMode(pushClose, INPUT); // set pin as intput for pushbutton to manually close the coop door
digitalWrite(DoorOpenLed, LOW); // Keep both LED's off at startup
digitalWrite(DoorClosedLed, LOW);
digitalWrite(openRelay, HIGH); // keep the motor off at start-up by keeping both HIGH
digitalWrite(closeRelay, HIGH);
digitalWrite(flash1Relay, HIGH); // keep the flash-lights off at start-up by keeping both HIGH
digitalWrite(flash2Relay, HIGH);
printInitiate(); // Write the "Initiate message to the OLED
screenON(); // Update all other info on the OLED and swith it on for the duration set in the settings above
}
/* ---------------------- Main Program Section ------------------ */
void loop() {
getTime();
if (analogRead(screenSwitch) >700) {screenON();} // Check if we need to switch the screen on for a moment. 433Mhz RC does not work during 'screenOn time' due to a conflict in libraries.
if(myNow==0){getRiseAndSet(); printSunrise(); printSunset(); printDate();} // At the start of a new day (at zero hours) we update the Sunrise & Sunset data
// Below the logic deciding the opening & closing of the door
// myDoorStatus = 1 => Door open
// myDoorStatus = 2 => Door opening
// myDoorStatus = 3 => Door closed
if((myNow == mySunrise) && (myDoorStatus != 1)) {openDoor(); }
if((myNow == myPause) && (myDoorStatus == 2)) {digitalWrite(DoorOpenLed, HIGH); closeDoor();}
if((myNow == mySunset) && (myDoorStatus != 3)) {closeDoor();}
if((digitalRead(pushClose) == HIGH) && (myDoorStatus != 3)) {closeDoor();}
if((digitalRead(pushOpen) == HIGH) && (myDoorStatus != 1)) {openDoor();}
// Below the logic for the intrudor alerts
if((enableFlash1==1) && (digitalRead(obstacleFlash1) == LOW) && (myNow > mySunset)) {flash1();}
if((enableFlash1==1) && (digitalRead(obstacleFlash1) == LOW) && (myNow < mySunrise)) {flash1();}
if((enableFlash2==1) && (digitalRead(obstacleFlash2) == LOW) && (myNow > mySunset)) {flash2();}
if((enableFlash2==1) && (digitalRead(obstacleFlash2) == LOW) && (myNow < mySunrise)) {flash2();}
if((myNow < myPause) && (myPause!=0)){
digitalWrite(DoorClosedLed, !digitalRead(DoorClosedLed)); // Alternating the 'open' and 'closed' LED's to show the door is paused
digitalWrite(DoorOpenLed, !digitalRead(DoorOpenLed));
}
checkRemote(); // Catching the remote control signal every second
stopDoor();
delay(1000); // Create a 1 second delay in order to avoid 'panic mode'
}// loop end
/* ---------------------- Program Funtions ------------------ */
/* ---------------------- Door Funtions ------------------ */
void openDoor()
{
digitalWrite(openRelay, LOW); // turn relay 1 ON
digitalWrite(closeRelay, HIGH); // turn relay 2 OFF
printOpening();
myCounter=myDoorDuration;
// loop to make the LED blink while the door is opening
for (int i = 0; i <= myDoorDuration*2; i++) {
if ( (i % 2) == 0) { myCounter=myCounter-1; u8x8.setCursor(8, 7); u8x8.print(" "); u8x8.setCursor(8, 7);u8x8.print(myCounter);u8x8.setCursor(11, 7);u8x8.print("sec."); }
digitalWrite(DoorOpenLed, !digitalRead(DoorOpenLed));
getTime();
//printTime();
//checkRemote();
delay(500);
}
myDoorStatus = 1; // Status=1 ==> Door is open
digitalWrite(DoorClosedLed, LOW);
digitalWrite(DoorOpenLed, HIGH);
resetCounter;
printOpen();
}//openDoor()
void closeDoor()
{
//startDoorMovement = millis();
digitalWrite(openRelay, HIGH); // turn relay 1 OFF
digitalWrite(closeRelay, LOW); // turn relay 2 ON
myDoorStatus = 2; // Status=2 ==> Door is closing
printClosing();
if (myPause!=0) {closingDuration=myDoorDuration-pausedAfter; myPause=0;} else {closingDuration=myDoorDuration;}
myCounter=closingDuration;
for (int i = 0; i <= closingDuration*2; i++) {
if ( (i & 0x01) == 0) { myCounter=myCounter-1; u8x8.setCursor(8, 7); u8x8.print(" "); u8x8.setCursor(8, 7);u8x8.print(myCounter);u8x8.setCursor(11, 7);u8x8.print("sec.");}
printCounter;
digitalWrite(DoorClosedLed, !digitalRead(DoorClosedLed));
doorDetection = digitalRead(obstacleDoor);
getTime();
//printTime();
//checkRemote();
if(doorDetection == LOW){ getTime();
myPause=myNow+closingPause;
pausedAfter=round((i/2)+0.5); // adding 0.5 will ensure we round the number up or down correctly as 'round' always rounds down.
printPaused();
digitalWrite(DoorClosedLed, HIGH);
digitalWrite(DoorOpenLed, LOW);
stopDoor();
return;
}
delay(500);
}
myDoorStatus = 3; // Status=3 ==> Door is closed
digitalWrite(DoorOpenLed, LOW);
digitalWrite(DoorClosedLed, HIGH);
resetCounter;
printClosed();
}//closeDoor()
void stopDoor()
{
digitalWrite(openRelay, HIGH); // turn relay 1 OFF
digitalWrite(closeRelay, HIGH); // turn relay 2 OFF
resetCounter();
}//stopDoor()
/* ---------------------- Flash light Funtions ------------------ */
void flash1()
{
printFlash1();
digitalWrite(flash1Relay, LOW);
delay(flash1duration*1000);
digitalWrite(flash1Relay, HIGH);
if(myDoorStatus==1){printOpen();}
if(myDoorStatus==3){printClosed();}
}
void flash2()
{
printFlash2();
digitalWrite(flash2Relay, LOW);
delay(flash1duration*1000);
digitalWrite(flash2Relay, HIGH);
if(myDoorStatus==1){printOpen();}
if(myDoorStatus==3){printClosed();}
}
/* ---------------------- Remote Control Funtion ------------------ */
void checkRemote()
{
if (enableRemote==1){
/* Serial.print("Received ");
Serial.print( mySwitch.getReceivedValue() );
Serial.print(" / ");
Serial.print( mySwitch.getReceivedBitlength() );
Serial.print("bit ");
Serial.print("Protocol: ");
Serial.println( mySwitch.getReceivedProtocol() ); */
u8x8.setCursor(8, 7); u8x8.print(" "); u8x8.setCursor(8, 7);u8x8.print("remote");
if ((mySwitch.getReceivedValue()==remoteDoorOpen) && (myDoorStatus != 1)) {openDoor(); }
if ((mySwitch.getReceivedValue()==remoteDoorClose) && (myDoorStatus != 3)) {closeDoor(); }
if ( mySwitch.getReceivedValue()==remoteDoorStop) {stopDoor(); }
if ( mySwitch.getReceivedValue()==remoteLightFlash) {flash1(); flash2(); }
mySwitch.resetAvailable();
}
} //checkRemote
/* ---------------------- Time related Funtions ------------------ */
void getTime()
{
DateTime now = rtc.now();
year = now.year();
month = now.month();
day = now.day();
hour = now.hour();
minute = now.minute();
second = now.second();
myNow = (hour*60)+minute; // Create a "minutes after midnight" integer in order to easily compare with the sunRise & sunSet from Dusk2Dawn
}//getTime
void getRiseAndSet()
{
getTime(); // Get my local Sunrise and Sunset time for today
mySunrise = myLocation.sunrise(year, month, day, true);
mySunset = myLocation.sunset(year, month, day, true);
char time[6]; // Calculate the local Sunrise + Sunset hours and minutes (as the function provides minutes after midnight) so we can print them on the OLED
Dusk2Dawn::min2str(time, mySunrise);
mySunrise = mySunrise + sunRiseOffset;
mySRHR = int(mySunrise/60);
mySRMN = int(mySunrise - (mySRHR*60));
Dusk2Dawn::min2str(time, mySunset);
mySunset = mySunset + sunSetOffset;
mySDHR = int(mySunset/60);
mySDMN = int(mySunset - (mySDHR*60));
}//getRiseAndSet
/* ---------------------- OLED screen messages Funtions ------------------ */
// If ever you want to translate or modify the text shown on the OLED, this is the place to be ;-)
void screenON()
{
u8x8.setPowerSave(0); // Switch-on the OLED (Remote control messages are received, but not executed until the screen switches-off...
printMain();
getRiseAndSet(); // Get sunRise & sunSet for today
printSunrise();
printSunset();
printDate();
printTemp();
for (int i = 0; i <= screenOnTime; i++) {
getTime();
printTime();
delay(1000); // Sow the time pogressing whilst the OLED is on.
}
u8x8.setPowerSave(1); // Switch-off the OLED
}// screenON
void printMain()
{
u8x8.setCursor(0,0); u8x8.print("Date:");
u8x8.setCursor(0,1); u8x8.print("Time:");
u8x8.setCursor(0,3); u8x8.print("Sunrise | Sunset");
u8x8.setCursor(8,4); u8x8.print("|");
u8x8.setCursor(0,6); u8x8.print("---- Status: ---");
}//printMain
void printInitiate(){u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Initiate");} //initiate the status. Press the open or close button (depending actual door status) to change and capture the correct status
void printOpening() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Opening ");} //open
void printOpen() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Open ");} //opening
void printClosing() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Closing ");} //closing
void printClosed() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Closed ");} //closed
void printPaused() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Paused ");} //paused
void printFlash1() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Flash 1 ");} //printFlash1
void printFlash2() {u8x8.setCursor(0, 7); u8x8.print(" "); u8x8.setCursor(0, 7);u8x8.print("Flash 2 ");} //printFlash2
void printCounter() {u8x8.setCursor(8, 7); u8x8.print(" "); u8x8.setCursor(8, 7);u8x8.print(myCounter);u8x8.setCursor(11, 7);u8x8.print("sec."); }//printCounter
void resetCounter() {u8x8.setCursor(8, 7); u8x8.print(" ");}//resetCounter
void printDate()
{
u8x8.setCursor(5,0);
if (day<=9) {u8x8.print("0");} u8x8.print(day); u8x8.print("-");
if (month<=9){u8x8.print("0");} u8x8.print(month);u8x8.print("-");
u8x8.print(year);
}// printDate()
void printTime()
{
//u8x8.setPowerSave(1);
u8x8.setCursor(5,1);
if (hour<=9) {u8x8.print("0");} u8x8.print(hour); u8x8.print(":");
if (minute<=9){u8x8.print("0");} u8x8.print(minute);u8x8.print(":");
if (second<=9){u8x8.print("0");} u8x8.print(second);
} // printTime();
void printSunrise()
{
u8x8.setCursor(1, 4); if (mySRHR<=9){u8x8.print("0");} u8x8.print(mySRHR);
u8x8.setCursor(3,4); u8x8.print(":");
u8x8.setCursor(4,4); if (mySRMN<=9){u8x8.print("0");} u8x8.print(mySRMN);
}//printSunrise
void printSunset()
{
u8x8.setCursor(10, 4); if (mySDHR<=9){u8x8.print("0");} u8x8.print(mySDHR);
u8x8.setCursor(12,4); u8x8.print(":");
u8x8.setCursor(13,4); if (mySDMN<=9){u8x8.print("0");}u8x8.print(mySDMN);
}//printSunset
void printTemp()
{
sensors.requestTemperatures(); // Send the command to get temperatures
myTemp = round(sensors.getTempCByIndex(0)+0.5); // If you want to show Fahrenheit, you need to change the C to F
//Serial.print(myTemp); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire
u8x8.setCursor(13, 7); u8x8.print(" "); u8x8.setCursor(13, 7);u8x8.print(myTemp);u8x8.setCursor(15, 7);u8x8.print("C");
}//printTemp
/*
*/
Comments