I have seen a bunch of threads for folks looking for solutions for controlling garage doors via SmartThings and I just finished mine this weekend, so I thought I would share.
My solution was to use an Arduino Uno with SmartThings Shield, 2-channel relay, and 2 magnetic reed switches. Total cost of all parts was about $85 plus some misc wire to connect everything up. This controls two garage doors and provides feedback as to their state (open/closed).
Here’s the parts list:
- Arduino Uno
- Power Adapter (for the Uno)
- 2-Channel Relay
- 2x Magnetic Reed Switches
- SmartThings Arduino Shield
Two Caveats: 1) My Uno came in a starter kit I bought a while ago, so the one linked to above is not the exact one I have, but it should work the same. 2) The reed switches here are not the exact ones I bought (I got mine at Fry’s) But they look nearly identical to these.
Two pins on the Arduino are hooked to the inputs for the relay switches. When that pin goes high, it closes the relay. I then hooked the relay’s normally open connection to the same two inputs on the garage door motor where my manual wall switch hooks in (I left the wall switch still there, so there are now two wires into each of the two connections on the garage door motor). When the Arduino receives the “pushLeft” or “pushRight” command - it closes the left or right relay for 1 sec to simulate someone pushing the physical button for 1 sec. This actuates the garage door.
Two other pins on the Arduino are used to read the state of the magnetic reed switches. I use the pins on the Arduino in INPUT_PULLUP mode, which means the Arduino holds them high with a pull-up resistor and when the reed switch closes, it gets pulled low (to ground). This simply means when I read the pins in the Arduino code, a HIGH indicates the door is open, and LOW means it’s closed. The “common” side of the reed switch is just hooked to ground on the Arduino board.
I put the whole thing in a plastic project box and zip-tied it to one of the brackets which mount one of the garage door motors to the ceiling.
The device controller is pretty simple - it just has two buttons to actuate each of the garage doors. And it looks like this:
The Device Type Code has two custom attributes: leftDoor and rightDoor, and two custom commands: pushLeft and pushRight. The Code is:
/**
* Garage Door
*
* Author: Steve Sell
* Date: 2014-02-02
*/
metadata {
// Preferences
// tile definitions
tiles {
standardTile("leftDoor", "device.leftDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
state "closed", label: 'Closed', action: "pushLeft", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState: "opening"
state "open", label: 'Open', action: "pushLeft", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e", nextState: "closing"
state "opening", label: "Opening", icon: "st.doors.garage.garage-opening", backgroundColor: "89C2E8"
state "closing", label: "Closing", icon: "st.doors.garage.garage-closing", backgroundColor: "89C2E8"
}
standardTile("rightDoor", "device.rightDoor", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
state "closed", label: 'Closed', action: "pushRight", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState: "opening"
state "open", label: 'Open', action: "pushRight", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e", nextState: "closing"
state "opening", label: "Opening", icon: "st.doors.garage.garage-opening", backgroundColor: "89C2E8"
state "closing", label: "Closing", icon: "st.doors.garage.garage-closing", backgroundColor: "89C2E8"
}
main "leftDoor"
details(["leftDoor","rightDoor"])
}
simulator {
status "on": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
status "off": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
// reply messages
reply "raw 0x0 { 00 00 0a 0a 6f 6e }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
reply "raw 0x0 { 00 00 0a 0a 6f 66 66 }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
}
}
Map parse(String description) {
def name = null
def value = zigbee.parse(description)?.text
log.debug "Value is ${value}"
def linkText = getLinkText(device)
def descriptionText = getDescriptionText(description, linkText, value)
def handlerName = value
def isStateChange = value != "ping"
def displayed = value && isStateChange
def incoming_cmd = value.split()
name = incoming_cmd[0]
value = incoming_cmd[1]
def result = [
value: value,
name: value != "ping" ? name : null,
handlerName: handlerName,
linkText: linkText,
descriptionText: descriptionText,
isStateChange: isStateChange,
displayed: displayed
]
log.debug result
result
}
def pushLeft() {
zigbee.smartShield(text: "pushLeft").format()
}
def pushRight() {
zigbee.smartShield(text: "pushRight").format()
}
The Arduino Code is:
//*****************************************************************************
#include <SoftwareSerial.h> //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>
//*****************************************************************************
// Pin Definitions | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
#define PIN_LED 13
#define PIN_THING_RX 3
#define PIN_THING_TX 2
#define PIN_RIGHT 4
#define PIN_RIGHT_CONTACT 9
#define PIN_LEFT 7
#define PIN_LEFT_CONTACT 8
#define OPEN HIGH
#define CLOSED LOW
#define PUSH_DELAY 1000 // milliseconds to keep the button "pushed"
//*****************************************************************************
// Global Variables | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
SmartThingsCallout_t messageCallout; // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout); // constructor
bool leftClosed, rightClosed;
bool isDebugEnabled; // enable or disable debug in this example
int stateLED; // state to track last set value of LED
int stateNetwork; // state of the network
//*****************************************************************************
// Local Functions | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void pushLeft()
{
smartthing.shieldSetLED(0, 0, 2); // blue
digitalWrite(PIN_LEFT,LOW);
delay(PUSH_DELAY);
digitalWrite(PIN_LEFT,HIGH);
smartthing.shieldSetLED(0, 0, 0); // off
}
//*****************************************************************************
void pushRight()
{
smartthing.shieldSetLED(0, 0, 2); // blue
digitalWrite(PIN_RIGHT,LOW);
delay(PUSH_DELAY);
digitalWrite(PIN_RIGHT,HIGH);
smartthing.shieldSetLED(0, 0, 0); // off
}
bool isClosed(int pin)
{
return (digitalRead(pin) == CLOSED);
}
void updateDoorState()
{
if (leftClosed != isClosed(PIN_LEFT_CONTACT))
{
leftClosed = isClosed(PIN_LEFT_CONTACT);
if(leftClosed)
{
smartthing.send("leftDoor closed");
Serial.println("leftDoor closed");
} else {
smartthing.send("leftDoor open");
Serial.println("leftDoor open");
}
}
if (rightClosed != isClosed(PIN_RIGHT_CONTACT))
{
rightClosed = isClosed(PIN_RIGHT_CONTACT);
if(rightClosed)
{
smartthing.send("rightDoor closed");
Serial.println("rightDoor closed");
} else {
smartthing.send("rightDoor open");
Serial.println("rightDoor open");
}
}
}
//*****************************************************************************
void setNetworkStateLED()
{
SmartThingsNetworkState_t tempState = smartthing.shieldGetLastNetworkState();
if (tempState != stateNetwork)
{
switch (tempState)
{
case STATE_NO_NETWORK:
if (isDebugEnabled) Serial.println("NO_NETWORK");
smartthing.shieldSetLED(2, 0, 0); // red
break;
case STATE_JOINING:
if (isDebugEnabled) Serial.println("JOINING");
smartthing.shieldSetLED(2, 0, 0); // red
break;
case STATE_JOINED:
if (isDebugEnabled) Serial.println("JOINED");
smartthing.shieldSetLED(0, 0, 0); // off
break;
case STATE_JOINED_NOPARENT:
if (isDebugEnabled) Serial.println("JOINED_NOPARENT");
smartthing.shieldSetLED(2, 0, 2); // purple
break;
case STATE_LEAVING:
if (isDebugEnabled) Serial.println("LEAVING");
smartthing.shieldSetLED(2, 0, 0); // red
break;
default:
case STATE_UNKNOWN:
if (isDebugEnabled) Serial.println("UNKNOWN");
smartthing.shieldSetLED(0, 2, 0); // green
break;
}
stateNetwork = tempState;
}
}
//*****************************************************************************
// API Functions | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void setup()
{
// setup default state of global variables
isDebugEnabled = true;
stateLED = 0; // matches state of hardware pin set below
stateNetwork = STATE_JOINED; // set to joined to keep state off if off
// setup hardware pins
pinMode(PIN_LED, OUTPUT); // define PIN_LED as an output
pinMode(PIN_RIGHT, OUTPUT);
pinMode(PIN_LEFT, OUTPUT);
digitalWrite(PIN_RIGHT, HIGH);
digitalWrite(PIN_LEFT, HIGH);
digitalWrite(PIN_LED, LOW); // set value to LOW (off) to match stateLED=0
pinMode(PIN_LEFT_CONTACT, INPUT_PULLUP);
pinMode(PIN_RIGHT_CONTACT, INPUT_PULLUP);
if (isDebugEnabled)
{ // setup debug serial port
Serial.begin(9600); // setup serial with a baud rate of 9600
Serial.println("setup.."); // print out 'setup..' on start
}
// Get the Current State of the Doors
Serial.println("Getting Door State...");
if (isClosed(PIN_LEFT_CONTACT))
{
leftClosed = true;
smartthing.send("leftDoor closed");
Serial.println("leftDoor closed");
} else {
leftClosed = false;
smartthing.send("leftDoor open");
Serial.println("leftDoor open");
}
delay(1000);
if (isClosed(PIN_RIGHT_CONTACT))
{
rightClosed = true;
smartthing.send("rightDoor closed");
Serial.println("rightDoor closed");
} else {
rightClosed = false;
smartthing.send("rightDoor open");
Serial.println("rightDoor open");
}
}
//*****************************************************************************
void loop()
{
// run smartthing logic
smartthing.run();
// Check the open/closed state of the doors
updateDoorState();
// Code left here to help debut network connections
setNetworkStateLED();
}
//*****************************************************************************
void messageCallout(String message)
{
// if debug is enabled print out the received message
if (isDebugEnabled)
{
Serial.print("Received message: '");
Serial.print(message);
Serial.println("' ");
}
if (message.equals("pushLeft"))
{
pushLeft();
}
else if (message.equals("pushRight"))
{
pushRight();
}
}