N9WIB Notebook


Chicken Coop Automatic Door

The goal is to automate the opening and closing of a chicken coop door twice daily based on sunrise and sunset data stored in a mysql database and python code.  A linear actuator, Raspberry Pi Zero W and Adafruit DRV8871 DC Motor Driver are used in this project.

DOOR CONFIGURATION

A 1x8x16 inch piece of standard pine board was mounted to two lateral 2x4 studs with cabinet drawer slides. The door can easily be moved up and down while mounted on the drawer slides.

LINEAR ACTUATOR

An ECO LLC 12 Inch Linear Actuator Motor 1000N DC12V was purchased from Amazon for less than $50 dollars. The linear actuator motor drives a shaft that can extend or retract a 12 inch distance. The shaft of the linear actuator was mounted to the pine board and allows for vertical opening and closing of the door. This provides more stability than a motor and chain or rope. The linear actuator runs on 12 v DC and has a reported max current draw of 3 Amps. Extension or retraction of the actuator is dependent on the polarity of DC supplied to the motor. If you want the shaft to retract while extended then switch the power supply lead polarity to manually control the motor. Application of the power and changing polarity will be controlled by the Adafruit motor control board. A standard switching power supply rated at 12 v and 5 Amps was purchased from Amazon to drive the motor.

ADAFRUIT DRV8871 DC MOTOR DRIVER

ADAFRUIT
Adafruit DRV8871 DC MOTOR DRIVER

The motor controller is able to handle up to 3.6 amps peak current with a motor voltage range of 6.5V to 45V and up to 5.5V logic level on IN pins. A motor controller is necessary since the raspberry pi itself would not be able to supply the needed current or voltage to run the motor. An independent power source is hooked up to the motor controller board to power the motor. Wiring is performed as follows

* Note: The Power +/- terminals are physically connected to Vm and Gnd. Do NOT connect a higher voltage power supply to the power terminals and connect the Vm and Gnd to the Raspberry Pi as I did after misreading the wiring diagram. This will promptly send the 12v and higher current into your pi and fry it. Nothing like the smell or burning electronics and a scorching hot pi. Just use the Power terminals which will supply the motor driver and your motor. The board does NOT need additional power from your pi

RASPBERRY PI ZERO W

Setting the Raspberry pi output pins to high or low (3.3v or 0.0v) will control whether the linear actuator shaft extends or retracts.

Raspberry Pi with Motor Controller
Power Supply with Motor Controller and Raspberry Pi
Completed Chicken Coop

The python script will call a function that either retracts or extends the coop door depending on time of day and a database table of sunrise and sunset data.


#!/usr/bin/python3

"""
Controls the extension and retraction of a linear actuator to open and close a chicken coop door.
Set 2 Raspberry Pi GPIO pins to output high or low at 3.3 volts to run a motor controller.
The motor controller will be activated twice daily based on sunrise and sunset data stored in a mysql database table. 
"""

import os
import RPi.GPIO as GPIO
import time
import datetime
import pymysql 

# clear screen

os.system("clear")

# variables

IN1 = 13
IN2 = 18

# intro

print(f"DOOR CONTROLER SCRIPT IS STARTING \n")

# set the GPIO pin numbering reference to actual pin count
# set the GPIO pin to output 

GPIO.setmode(GPIO.BOARD)
GPIO.setup(IN1, GPIO.OUT)
GPIO.setup(IN2, GPIO.OUT)

## FUNCTIONS ##

# entend the control arm. close the coop door

def extend():

   GPIO.output(IN1, GPIO.HIGH)
   GPIO.output(IN2, GPIO.LOW)

   IN1_state = GPIO.input(IN1)
   IN2_state = GPIO.input(IN2)

   print(f"IN1 is {IN1_state} IN2 is {IN2_state} \n")

   # run the motor the time it takes to extend the arm

   time.sleep(22)

   off()

# retract the control arm. open the door

def retract():

   GPIO.output(IN1, GPIO.LOW)
   GPIO.output(IN2, GPIO.HIGH)

   IN1_state = GPIO.input(IN1)
   IN2_state = GPIO.input(IN2)

   print(f"IN1 is {IN1_state} IN2 is {IN2_state} \n")

   # run the motor for the amount of time it takes to fully retract the arm

   time.sleep(22)

   off()

# turn both pins to LOW or OFF so the motor does nothing

def off():

   GPIO.output(IN1, GPIO.LOW)
   GPIO.output(IN2, GPIO.LOW)

   IN1_state = GPIO.input(IN1)
   IN2_state = GPIO.input(IN2)

   print(f"IN1 is {IN1_state} IN2 is {IN2_state} \n")

# connect to the local database

def db_connect():

        try:

            connection = pymysql.connect(
                host='localhost',
                database='barn',
                user='pibot',
                password='xxxxx',
                cursorclass=pymysql.cursors.DictCursor)
        
            db_connect.connection = connection            

        except Exception as e:

            print(f"Failed Connecting to Database")
            sys.exit(1)

# query the database for the sunrise and sunset data for todays date

def db_query():

    while True:
    
        connection = db_connect.connection
        
        # get current date

        current_date_time = datetime.datetime.now()
        current_date = datetime.date(current_date_time.year,current_date_time.month,current_date_time.day)

        with connection.cursor() as cursor:

            sql = "SELECT date, sunrise, sunset FROM solar_data WHERE date = current_date"
            cursor.execute(sql)
            result = cursor.fetchone()

            # get sunrise sunset times from database for current date

            solar_date = result['date']
            sunrise = result['sunrise']
            sunset = result['sunset']

            # format sunrise and sunset data in datetime format

            sunrise_dt = datetime.datetime.strptime(f"{solar_date} {sunrise}", "%Y-%m-%d %H:%M:%S")
            sunset_dt = datetime.datetime.strptime(f"{solar_date} {sunset}", "%Y-%m-%d %H:%M:%S")

            # calculate time difference in minutes between current time and sunset, sunrise

            sunrise_delta = current_date_time - sunrise_dt
            sunrise_delta_min = sunrise_delta.total_seconds()/60
            sunrise_delta_min = abs(int(round(sunrise_delta_min)))

            sunset_offset_minutes = datetime.timedelta(minutes=120) # make the door close later than sunset
            sunset_delta = current_date_time - (sunset_dt + sunset_offset_minutes)
            sunset_delta_min = sunset_delta.total_seconds()/60
            sunset_delta_min = abs(int(round(sunset_delta_min)))

            # print and log output

            log_output_routine = f"TIME NOW: {current_date_time} SUNRISE: {sunrise} SUNSET: {sunset} DELTA SUNRISE: {sunrise_delta_min} DELTA SUNSET: {sunset_delta_min} \n" 

            print(log_output_routine) 

            f = open("../logs/door_controller_log.txt","a+")
            f.write(log_output_routine)
            f.close()

            # open the door if sunrise

            if sunrise_delta_min < 5:

                retract()

                print(f"*** RETRACT COOP DOOR CALLED ***")

                f = open("../logs/door_controller_log.txt","a+")
                f.write("** Door Retract Called ** \n")
                f.close()

                time.sleep(600)

            # close the door after sunset

            elif sunset_delta_min < 5:

                extend()

                print(f"*** EXTEND COOP DOOR CALLED ***")

                f = open("../logs/door_controller_log.txt","a+")
                f.write(log_output_routine)
                f.write("** Door Extend Called ** \n")
                f.close()

                time.sleep(600)

        # repeat the script every minute

        time.sleep(60)

    cursor.close()
    connection.close()
    
    f.close()

## MAIN SCRIPT ##

# connect to database

db_connect()

# query the database and run the script

db_query()


GPIO.cleanup()