elbie
Published © GPL3+

Automatic Chicken Coop Door (Porte Automatique Poulailler)

Automatically open and close the chicken coop door (poulailler in French) at local sunrise and sunset as well manually if/when needed.

IntermediateProtip872
Automatic Chicken Coop Door (Porte Automatique Poulailler)

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Chip Antenna, 433 MHz
Chip Antenna, 433 MHz
×1
Graphic OLED, 128 x 64 Pixels
Graphic OLED, 128 x 64 Pixels
×1
Real Time Clock (RTC)
Real Time Clock (RTC)
×1
Timer, Oscillator & Pulse Generator IC
Timer, Oscillator & Pulse Generator IC
×2
Level Shifter Board
SparkFun Level Shifter Board
×1
4-port Relay Board
PHPoC 4-port Relay Board
×1
Rechargeable Battery, 12 V
Rechargeable Battery, 12 V
×1
Plastic Enclosure, Project Box
Plastic Enclosure, Project Box
×1
Phone Audio Connector, Jack
Phone Audio Connector, Jack
×7
SparkFun Benchtop Power Board Kit
SparkFun Benchtop Power Board Kit
×1
Perma-Proto Breadboard Half Size
Perma-Proto Breadboard Half Size
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Proximity Sensor
Proximity Sensor
×3
Linear Solenoid, 12.2 VDC
Linear Solenoid, 12.2 VDC
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×3
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1
LED Strip, 4 LEDs
LED Strip, 4 LEDs
×2
Test Accessory, AC Power Adapter
Test Accessory, AC Power Adapter
×1

Story

Read more

Schematics

Chicken

Code

Chicken

C#
Automatic Chicken coop door operating system
#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                 // My chicken coop location
#define myLongtitude      3
#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         = 0;                 // Number of minutes the coop door will open 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);
  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.

  if (!rtc.isrunning()) {
   u8x8.setCursor(0,0); u8x8.print("RTC problem");
   Serial.println("RTC lost power, lets set the time!");
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));              
  }

  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


/*
*/

Chicken

Credits

elbie

elbie

0 projects • 0 followers

Comments