Automatic Garage Door Closer

I purchased an Arduino about a month ago, and have basically just been toying around with it. I went to undergrad for bioengineering, but got next to zero experience programming. Nevertheless, I picked it up quickly(ish) enough in order to do a small project.

I share the garage with a couple of my roommates, and a consistent source of stress is when one of us leaves the garage door open after leaving the house. Thankfully, nothing has been stolen (yet) but I thought it might be nice to use the Arduino to automatically close the garage door if we leave it open for more than 5 minutes.

I've attached a brief circuit sketch (from http://www.digikey.com/schemeit -- neat tool if you don't have actual circuit schematic software). I've completed the circuit, but still need to install the "sensor", which will just connect 5V to the status_pin on the Arduino when the garage door is open.

Sketch:

/* Garage Timer
 This sketch is meant to continuously monitor the status of the 
 garage door. If it is opened, the sketch will wait 5 minutes to 
 see if the garage door is still open. If it is still open, the relay 
 will be activated in order to close the garage door. A red LED
 (open_door_led) will also be lit if the Arduino has had to close
 the garage door.
 */

#include <Time.h>

const long oneSecond = 1000;
const long oneMinute = oneSecond * 60;
int status_pin = 7;  // open (HIGH) or closed (LOW)

int operate_pin = 8;  // relay to open/close door
int open_door_led = 9; // LED to indicate door was left open today.

int door_count = 0; // Keep track of how many times the door has been left open.
int door = LOW;

void setup() 
{
  Serial.begin(9600);
  pinMode(status_pin, INPUT);

  pinMode(operate_pin, OUTPUT);
  pinMode(open_door_led, OUTPUT);
}

void loop()
{
  door = digitalRead(status_pin);
  
  if(door == LOW) // Door is closed.  Nothing to see here!
  {
    Serial.println("The door is closed.  Keep up the good work!");
    delay(10 * oneSecond);
  }
  else  // door is open
  {
    Serial.println("The door is open.  I'll give you 5 minutes to shut it!");
    delay(5 * oneMinute);  // giving you a chance to close door on your own.
    door = digitalRead(status_pin);
    if(door == LOW)
    {
      Serial.println("Good, you closed the door.  Whew!");
    }
    else
    {
      Serial.println("Looks like you forgot to close the garage door. Closing the door now.");
      digitalWrite(operate_pin, HIGH);  // Closing the door
      delay(100);
      digitalWrite(operate_pin, LOW); // Stop closing door
      door_count = door_count + 1;
      digitalWrite(open_door_led, HIGH);
      delay(20 * oneSecond);
      Serial.println("The door should be shut now.");
      Serial.print("The door has been left open ");
      Serial.print(door_count);
      Serial.println(" time(s).");
    }
  }
}

The "open_door_led" will let me know if the door has been left open at all and the Arduino had to close it. I can then just reset the Arduino to reset that LED so I know the next time it's left open. If I were a more motivated individual, I would include an LCD display to show more information (or, better yet, email me when the door was left open) and also log the times when the door was opened, and whether it was closed manually or if the Arduino had to do it.

Any comments, thoughts, or criticisms?

Sounds like a good project, I've done some thinking about something similar myself.

Regarding the circuit, the sensor switch is in the wrong place. When it closes, it will short the 5V supply to ground, not what we want. Plus the green LED will never illuminate, as it's connected to an input pin. As for the relay, without knowing the specs for the unit you have in mind, the circuit as shown will only put 5mA through the relay coil. Most relays require considerably more, in fact, most require more than the 20mA that an Arduino pin can safely supply. A small NPN junction transistor is usually a great way to drive a relay.

Regarding the code, it's fairly minimal, it looks like it will operate pretty much as you expect. Obviously, this is a project I'd test on the bench to ensure it works as expected before installing it on the garage door. I might add a couple things, one being an alarm. When the code decides it needs to close the door, I'd sound an audible alarm for maybe 30-60 sec first before activating the relay. I might also have an override switch to stop the controller from closing the door for those times when I wanted the door to stay open more than five minutes. Finally, when the controller decides to close the door, it's an open-loop situation, meaning that it activates the relay but then just assumes the door closes properly. Most garage doors have safety mechanisms that could make that a bad assumption. Mine has a light beam across the bottom that if interrupted, will prevent the door from closing. If too much force is required to close the door, i.e. something blocking it, or a spring breaks, etc., it will reverse, open the door, and stop. The upshot of all this is that the controller may not really be aware of the state of the door. I can make suggestions to address this if you're interested.

Oops, that was a drawing error showing the garage sensor shorting to ground. It shorts the 5V to the input pin, which is also connected to the LED so I get visual confirmation that the Arduino knows the door is open.

I did, however, make an error in putting the 1k resistor with the relay coil. I checked the spec sheet on the relay (a cheapo from RadioShack, model 05P10), and it seems I don't need the resistor at all (5V / coil resistant of 56 Ohms = 89.3 mA current, listed as nominal coil current). I had it connected without a resistor on the breadboard on my bench and it was working fine. I added the resistor thinking that I might be putting too much current through the coil. Is that 89.3 mA current too much for the Arduino to put out? Like I said, it was working for me, but if that will be stressing the Arduino too much, I can always use a different relay (I have one that requires only 15-20 mA, http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=110909482881&ssPageName=ADME:L:OC:US:3160, but I didn't want to waste 4 relays when I only need one).

I thought about adding a buzzer/alarm like you mentioned, but I didn't want to waste my only buzzer that came with my starter kit (I have a Mega on the way to replace my Uno once it's installed in the garage). I know what you mean about the garage door status. I considered adding a 2nd sensor to detect that the door closed, that way it doesn't assume it's closed if it's not completely opened. I was going to have it send me a message if it gets confused and the door is neither open nor closed, but I don't feel like running an Ethernet cable to the garage (and the WiFi shields are pricey!). I don't picture it being a problem often (if ever), and since the relay just drives the regular garage door operator contacts, it will still use the laser sights to prevent it from closing the door on something. I'll definitely consider adding that extra stuff in the future; right now I'd rather just get the thing installed and working.

My last thing to check is how much current is running through when I short the 2 pins for the garage door opener. My relay is rated for 1A at 120AC/24VDC, and I wouldn't think it's using more than that. Still, I'd rather check with the multimeter before I go and ruin my relay or start a fire!

Thanks for the feedback. Any other ideas?

Is that 89.3 mA current too much for the Arduino to put out? Like I said, it was working for me, but if that will be stressing the Arduino too much, I can always use a different relay (I have one that requires only 15-20 mA,

Yes, 90ma is too much to draw from a output pin. Either use a switching transistor with the existing relay or use the lower current relay. Output pin current should be limited to 20-30ma maximum current draw.

Lefty

retrolefty:
Yes, 90ma is too much to draw from a output pin. Either use a switching transistor with the existing relay or use the lower current relay. Output pin current should be limited to 20-30ma maximum current draw.

Lefty

Thanks. Switching to the other relay instead. Hopefully, RadioShack will let me return that other one if I can find the receipt.

Also, I just checked the garage door opener, and it's only showing ~35 mA when the 2 pins are shorted to operate the door, so no worries there.

I've done pretty much the same project, so here is a couple of suggestions:

Add a disable button/switch that will prevent it from closing while it's active if you need to keep it open for whatever reason.

Re-implement the timing to not use delays. You're better off detecting if it's been open for 5 minutes, than if it was open exactly 5 minutes apart. The way you have it now, two people arriving 5 minutes apart could trigger it to close prematurely.

Redoing this without delays also makes it easy to add like a 1 minute warning, be it audible or visible, to let you know that the garage it about to close, which I have also found useful.

Add a PIR on a input and have it read that input. If the input lets say goes high, have it reset your 5 minute timer back to start. That way if your in your garage or someone else is it will constantly reset it. I have the same exact thing made in my garage and it works great. The only difference is I used a dip switch and read it to determine the time frame to leave the door open. This way I can set it from 5 minutes to 1.5 hours.
Don't forget to add denounce to your disable switch. Otherwise it will false. I have code for that if you want it.

One last thing. Have the PIR hooked to a interrupt line. Have the interrupt reset the time via code.

I used your code to make a closer of my own, I put an ethernet board on so that there would be a web server. I created a selector button and LEDs to cycle from 5 to 15(or in my case I changed it to 20), 60 minute and off delays, rather than a dip switch. I hope you don't mind me posting it to this thread.

/* Arduino Garage Door Closer # 4
with ethernet shield and webserver

DIGITAL PINS USED
3 Status LEDs for Door Timer
1 Led for Door Open
1 Led for Door Closed
1 Pushbutton Switch for Door Status
1 Pushbutton Switch for timer selection
1 Relay Board 
Total = 8

*/


#include <SPI.h>
#include <Ethernet.h>

 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 177 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); //server port
 
String readString;

int doordelay; // Delay in minutes to close door if open
int doordelayminutes = 1;
const long oneSecond = 1000;
const long oneMinute = oneSecond * 60;
long doorinterval;

int on_off = 2;  // on/off switch pin
int status_pin = 7;  // open (HIGH) or closed (LOW) pin
int operate_pin = 6;  // relay pin to open/close door

int fivemin_led = 9; // (5) Five  Minute Timer LED pin
int fifteenmin_led = 8; // (15) Fifteen Minute Timer LED pin
int sixtymin_led = 5; // (60) Sixty Minute Timer LED pin

int open_door = 4; // Open Door LED pin
int closed_door = 3; // Closed Door LED pin

int door_count = 0; // Keep track of how many times the door has been left open.
int door = LOW;     // Door Status
int powertog = LOW; // Disable switch status
int buttonPushCounterm = 0;
int buttonStatem = 0;
int lastButtonStatem = 0;


int timer ;
int minutes_1 = 60000; // 1000*60
int minutes_20 = 1200000; // 1000*60*20
int minutes_240 = 14400000; // 1000*60*240
String buffer = "";  

unsigned long current_time;



void setup() 
{
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
  Serial.begin(9600);
  pinMode(status_pin, INPUT);
  pinMode(operate_pin, OUTPUT);
  digitalWrite(operate_pin, HIGH);
 }

void closedoor()
{ 
  digitalWrite(operate_pin, LOW);  // Closing the door
  analogWrite(fivemin_led, 255);  
  analogWrite(fifteenmin_led, 255);
  analogWrite(sixtymin_led, 255);
  delay(100);
  digitalWrite(operate_pin, HIGH); // Stop closing door
  analogWrite(fivemin_led, 0);  
  analogWrite(fifteenmin_led, 0);
  analogWrite(sixtymin_led, 0);
  delay(100);
  analogWrite(fivemin_led, 255);  
  analogWrite(fifteenmin_led, 255);
  analogWrite(sixtymin_led, 255);
  delay(100);
  analogWrite(fivemin_led, 0);  
  analogWrite(fifteenmin_led, 0);
  analogWrite(sixtymin_led, 0);
}  
  
void loop()
{  
 if(doordelay == 0 )  
          {
           doordelay=doordelayminutes;
          }
          
// Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
            
//read char by char HTTP request
        if (readString.length() < 100) {
 
//store characters to string
          readString += c;
//Serial.print(c);
        }
 
        //if HTTP request has ended
        if (c == '\n') {
 
          ///////////////
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();
 
          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<meta name=\"viewport\" content=\"width=device-width; initial-scale=1.0; maximum-scale=1.0;\">");
          client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
          client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
          client.println("<meta http-equiv=\"refresh\" content=\"15;URL='http://192.168.1.177/'\">");
          client.println("<link rel='stylesheet' type='text/css' href='http://homeautocss.net84.net/a.css' />");
          client.println("<TITLE>Garage Door</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<center>");
          client.println("<table style=\"font-family:verdana; margin:auto;\">");
          client.println("<tr>");   
          client.println("<td><H2><font face=\"verdana\">GARAGE DOOR </H2></font></td>");
          
          if(door == LOW )  
          {
           client.print("<td><H2><font face=\"verdana\" color=\"green\">CLOSED</font></H2></td>");
          }
          else
          {
           client.print("<td><H2><font face=\"verdana\" color=\"red\">OPEN</font></H2></td>");
          }
          client.println("</tr>");
          client.println("<tr>");

          if(buttonPushCounterm == 4 )  
          {
           client.print("<td><H3>CLOSER</td><td><H3>OFF</H3></td>");
          }
          else
          {
           client.print("<td><H3>CLOSER</td><td><H3>ON</H3></td>");
          }
          client.println("</tr>");
          client.println("<tr>");
          if(buttonPushCounterm == 4 )
          {
          }
          else
          {
          client.print("<td><H4>Delay</H4></td><td><H4>");
          client.print(doordelay);
          client.print(" Min</H4></td>");
          }
          client.println("</tr>");
          client.println("<tr>");
          client.print("<td><H4>Closings</H4></td><td><H4>");
          client.print(door_count);
          client.print(" Time(s)</H4></td>");
          client.println("</tr>");
          client.println("</table>");
          client.println("<table style=\"margin:auto;\">");
          client.println("<tr>");
          client.println("<a href=\"/?5min\"\">5 Min</a>

");
          client.println("</tr>");  
          client.println("<tr>");  
          client.println("<a href=\"/?20min\"\">20 Min</a>

");        
          client.println("</tr>");  
          client.println("<tr>");  
          client.println("<a href=\"/?60min\"\">60 Min</a>

");        
          client.println("</tr>");  
          client.println("<tr>");  
          client.println("<a href=\"/?999min\"\">Turn Off</a>

");        
          client.println("</tr>");  
          client.println("</table>");
          client.println("</center>");

          client.println("</BODY>");
          client.println("</HTML>");
 
          delay(1);
          //stopping client
          client.stop();
 
          if(readString.indexOf("?5min") >0)
          {
          doordelay=5;
          buttonPushCounterm = 1;
          }
        if(readString.indexOf("?20min") >0)
          {
          doordelay=20;
          buttonPushCounterm = 2;
          }
        if(readString.indexOf("?60min") >0)
          {
          doordelay=60;
          buttonPushCounterm = 3;
          }
           if(readString.indexOf("?999min") >0)
          {
          doordelay=999;
          buttonPushCounterm = 4;
          }
          //clearing string for next read
          readString="";
 
        }
      }
    }
  }


doorinterval = doordelay * oneMinute;
door = digitalRead(status_pin);
powertog = digitalRead(on_off);

if (powertog !=lastButtonStatem)
{
if (powertog == HIGH)
{
  buttonPushCounterm++;
  if (buttonPushCounterm == 5 )
  {
    buttonPushCounterm = 1;
  }
}
else
{
}
} 
lastButtonStatem = powertog;

if(buttonPushCounterm == 0)
{
  buttonPushCounterm = 2;
}
if(buttonPushCounterm == 1 )  
{ 
analogWrite(fivemin_led, 255);  
analogWrite(fifteenmin_led, 0);
analogWrite(sixtymin_led, 0);
doordelay=5;
}

if(buttonPushCounterm == 2 )  
{ 
analogWrite(fivemin_led, 255);  
analogWrite(fifteenmin_led, 255);
analogWrite(sixtymin_led, 0);
doordelay=20;
}
if(buttonPushCounterm == 3 )  
{ 
analogWrite(fivemin_led, 255);  
analogWrite(fifteenmin_led, 255);
analogWrite(sixtymin_led, 255);
doordelay=60;
}
if(buttonPushCounterm == 4 )  
{ 
analogWrite(fivemin_led, 0);  
analogWrite(fifteenmin_led, 0);
analogWrite(sixtymin_led, 0);
current_time = millis();
}
else
{
}

if(door == LOW )  
{ 
analogWrite(open_door, 0); 
analogWrite(closed_door, 255); 
current_time = millis();
}
else
{
analogWrite(open_door, 255);  
analogWrite(closed_door, 0); 
}


if( ((long)(millis() - (doorinterval + current_time)) >= 0) && powertog == LOW) // once  has past this condition should set an end for the timer.
    {
 closedoor();
 current_time = millis();
 door_count = door_count + 1;
    }
else
{
}
    
}

on another thread on this forum, there is discussion of the same concepts.

one idea was to have a timing that would know if the garage door was opened first, then the house door. that would indicate the need for a fast timing.

if the house door was opened, then the garage door, say to take out the trash, the sequence would indicate that someone is outside and the timing cycle would be longer.

the PIR in the garage would keep it open.

the problem with all this is that if a thief was fast enough into the garage after you arrived that they would trigger the pir and keep the door open.

another common sensor is a optical interrupter at a low level at the door. typically to prevent the door from closing when a child or thing is in the doorway.

you could tie into that and add that to your sequence.

garage door opens, house door opens, house door closes ( it is assumed the garage is empty of people)
then the light beam is tripped, that would beep in the house.

Hi

I have this functionality operating within my Arduino Home Automation system at www.2wg.co.nz. I implemented the garage door functionality early in the project - it has been fully operational for some months.

I use an Arduino controlled relay to open and close the door by bridging the door controller open/close circuit for 0.2 seconds.

I also use two reed switches - one to detect the door is fully closed and one to detect if the door is fully open. You can see real time values for my garage door at http://www.2wg.co.nz/36994/

When the closed switch changes I know that the door has just been opened and I can start various timers.

The garage door functionality operates in an active or inactive mode. In the active mode it will close the garage door if it is left open for four minutes. In inactive mode if the garage door is left open it will send a push email to my iphone after four minutes then after 8, 16, 32, 64, 128 minutes etc. That way if I am working on my car out in front of the house for a couple of hours my iPhone does not get dozens of emails.

I have implemented timer functionality using time calculations against the millis() function to avoid use of delay() (that stops an application) and can show you how to do that if necessary.

I also have a buzzer on my garage door that the arduino beeps when the door is opening and closing and for fifteen seconds before the automatic door close happens. It gives me time to move the car or myself if I am about to get hit by the door closing.

Also, if my system is in house alarm activation mode (I have PIRs to detect intruders) and the garage door is opened (e.g. with the wall switch) then my system sends an instant intruder push email to my iPhone.

Before I implemented the full functionality I did depart my house a couple of times leaving the garage door open. Since the functionality has been completed I have left it open once and on arriving at work noted the incoming push email - so I closed the garage remotely using system web site functionality only available after my password based login.

Happy to post a bit of code if you need it.

Cheers

Catweazle NZ

If the OP or anyone else has implemented a solution to the original problem, I would encourage you to proudly post photos, code, description of feature, lessons-learned and results.