OK Google, Open my Garage!

“OK, Google, can you please open my Garage door” . “Open Sesame”
You can do it too, and for a nerd stunt, the kids seem to think its pretty cool!  And yes, you can control it from your phone, and / or your Google Home!
So, I did this one out of a need, my Liftmaster garage door openers seemed to stop working.  I tried everything, new batteries, different remotes, even some antenna hackery, and still could not get them to work reliably.
If you google it, it seems to be a pretty normal occurrence, so I decided to share my solution.
The Solution
I used a Particle Photon wifi dev board, an LCD display, a couple components easily sourced online, IFTTT, openHAB, and of course Google. The result was that I could now control my garage doors (2 of them) from anywhere in the world, I could push a button, or even just ask Google to open/close them. Pretty cool eh?  Here is how I did it.
The Parts.
Particle PHOTON Wi-Fi Kit – http://geni.us/luuCR
Experiment Breadboard – http://geni.us/mycAwWK
Reed Switch (again 1 for each Garage Door) – http://geni.us/oTjq
Pull Down Resistors – http://geni.us/iYpx
Optional Serial Enabled 16×2 LCD – http://geni.us/nWTgbis
Putting it all together.
SMLXL

 The above image should give you enough to wire it up, there is not much there.  I used screw down connectors for the wires to the reed switches and the garage door opener, but you could connect them directly (I had them on hand).  The two LCC120 Digital relays are in the middle to the right, and each are wired to one garage door opener.
So the build was pretty simple.  I first picked a place in which I would put the brain (the Particle Core) . For me it made sense to go close to one of the door openers, but in the future I may move it over with the display.  I installed a reed switch on each garage door using hot glue and ran the wires back to one of the Garage door openers.  These get wired directly to an analog input pin on the Particle Core so that I can determine when the doors are open or closed.
The digital relay is what is used to fake the push of a button, when the Particle Core is used to turn it on by sending 3.3v to the relay, it then connects the other two pins which “push the button”.  This method ensures that we don’t have accidental button pushes in power failures etc.
From there we wired up the LCD display, this is really just for a status and somewhat redundant since you can just look and see which door is open/closed.  I had (and still might) intended to run this into the house so I had an indication of the door status from the back door.
SMLXL

I will let you follow the instructions to get your Particle Photon online (its super simple with a app on your phone) but once thats done you can login to your Particle account and we can copy the following code.  Take a look and make sure you understand it all, and there is some commented sections that we can use later to send/receive updates from openHAB.

////// BEGINNING OF CODE FOR GARAGE DOOR OPENERS
// This #include statement was automatically added by the Particle IDE.
#include <HttpClient.h>
#include “application.h”
/**
* Declaring the variables.
*/
unsigned int nextTime = 0; // Next time to contact the server
HttpClient http;
// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
{ “Content-Type”, “text/plain” },
{ “Accept” , “application/json”},
{ NULL, NULL } // NOTE: Always terminate headers will NULL
};
http_request_t request;
http_response_t response;
const String version = “v1.6”;
//Garage 1
const int pinReedSensor = A0;
const int pinRelayGND = D3;
const int pinRelaySignal = D2;
int isClosed = 1;
String garage1Status;
//Garage 2
const int pinReedSensor2 = A2;
const int pinRelayGND2 = D5;
const int pinRelaySignal2 = D4;
int isClosed2 = 1;
String garage2Status;
//Shared
long lastActionTime;
bool isCleared = false;
int screenOffDelay = 600000;//time before screen turns off when door is closed
int doorButtonDelay = 1000;
void setup()
{

//Garage 1
pinMode(pinRelayGND,OUTPUT);
digitalWrite(pinRelayGND, LOW);
pinMode(pinRelaySignal,OUTPUT);
digitalWrite(pinRelaySignal, LOW);

pinMode(pinReedSensor,INPUT);

//Garage 2
pinMode(pinRelayGND2,OUTPUT);
digitalWrite(pinRelayGND2, LOW);
pinMode(pinRelaySignal2,OUTPUT);
digitalWrite(pinRelaySignal2, LOW);

pinMode(pinReedSensor2,INPUT);
isClosed = digitalRead(pinReedSensor);
isClosed2 = digitalRead(pinReedSensor2);

Particle.function(“OperateDoor”, OperateDoor);

    // New code added October 25th 2016 to allow control open/close from another core
Particle.subscribe(“OperateDoor”, remoteControlOperation);
Particle.subscribe(“UpdateStatus”, remoteControlUpdate);

Particle.variable(“isClosed”, &isClosed, INT);
Particle.variable(“isClosed2”, &isClosed2, INT);

Particle.variable(“garage1Stat”, garage1Status);
Particle.variable(“garage2Stat”, garage2Status);

// //initialize screen
SetupScreen();
lcdCLEAR();

    sendMessageOne(“SmartGarage”);

delay(1000); //wait one sec to see the started message

sendMessageOne(“Smart Garage”);
sendMessageOne(“READY”);
lastActionTime = millis();
updateStatus();
}

void loop()
{
if(isClosed==0 && digitalRead(pinReedSensor)==1){
        sendMessageOne(“CLOSED – Garage 1”);
lastActionTime = millis();
isClosed = 1;
Particle.publish(“Garage1Status”,”CLOSED”);
sendUpdate(“/rest/items/contact_GarageDoor1″,”0”);
garage1Status = “CLOSE”;
}
    if(isClosed==1 && digitalRead(pinReedSensor)==0){
sendMessageOne(“OPEN – Garage 1”);
lastActionTime = millis();
isClosed = 0;
Particle.publish(“Garage1Status”,”OPEN”);
sendUpdate(“/rest/items/contact_GarageDoor1″,”1”);
garage1Status = “OPEN”;
}

//Door 2

if(isClosed2==0 && digitalRead(pinReedSensor2)==1){
sendMessageTwo(“CLOSED – Garage 2”);
lastActionTime = millis();
isClosed2 = 1;
Particle.publish(“Garage2Status”,”CLOSED”);
sendUpdate(“/rest/items/contact_GarageDoor2″,”0”);
garage2Status = “CLOSE2”;
}

if(isClosed2==1 && digitalRead(pinReedSensor2)==0){
sendMessageTwo(“OPEN – Garage 2”);
lastActionTime = millis();
isClosed2 = 0;
Particle.publish(“Garage2Status”,”OPEN”);
sendUpdate(“/rest/items/contact_GarageDoor2″,”1”);
garage2Status = “OPEN2”;
}

    if((lastActionTime + screenOffDelay)<millis()){
lcdOFF();
}
}
int i = 0;
void remoteControlOperation(const char *event, const char *data)
{
OperateDoor(data);
}
void remoteControlUpdate(const char *event, const char *data)
{
updateStatus();
}
int OperateDoor(String args){
int status_code = -1;

if(args == “OPEN” && isClosed==1){

sendMessageOne(“OPENING GARAGE 1”);
digitalWrite(pinRelaySignal, HIGH);
delay(doorButtonDelay);
digitalWrite(pinRelaySignal, LOW);
status_code = 1;
lastActionTime = millis();

}else if(args == “CLOSE” && isClosed == 0){

sendMessageOne(“CLOSING GARAGE 1”);
digitalWrite(pinRelaySignal, HIGH);
delay(doorButtonDelay);
digitalWrite(pinRelaySignal, LOW);
status_code = 0;
lastActionTime = millis();
}
//Door 2
if(args == “OPEN2” && isClosed2==1){
sendMessageTwo(“OPENING GARAGE 2”);
digitalWrite(pinRelaySignal2, HIGH);
delay(doorButtonDelay);
digitalWrite(pinRelaySignal2, LOW);
status_code = 3;
lastActionTime = millis();

}else if(args == “CLOSE2” && isClosed2 == 0){

sendMessageTwo(“CLOSING GARAGE 2”);
digitalWrite(pinRelaySignal2, HIGH);
delay(doorButtonDelay);
digitalWrite(pinRelaySignal2, LOW);
status_code = 4;
lastActionTime = millis();
}

return status_code;
}

void updateStatus (){
lastActionTime = millis();

if (digitalRead(pinReedSensor)==1) {
sendMessageOne(“CLOSED – GARAGE 1”);
isClosed = 1;
Particle.publish(“Garage1Status”,”CLOSED”);
sendUpdate(“/rest/items/contact_GarageDoor1″,”OFF”);
}else{
sendMessageOne(“OPEN – GARAGE 1”);
isClosed = 0;
Particle.publish(“Garage1Status”,”OPEN”);
sendUpdate(“/rest/items/contact_GarageDoor1″,”ON”);
}
if (digitalRead(pinReedSensor2)==1) {
sendMessageTwo(“CLOSED – GARAGE 2”);
isClosed2 = 1;
Particle.publish(“Garage2Status”,”CLOSED”);
sendUpdate(“/rest/items/contact_GarageDoor2″,”OFF”);

}else{
sendMessageTwo(“OPEN – GARAGE 2”);
isClosed2 = 0;
Particle.publish(“Garage2Status”,”OPEN”);
sendUpdate(“/rest/items/contact_GarageDoor2″,”ON”);
}
}

void SetupScreen(void){
Serial1.begin(9600);
delay(500);
}
//send string
void sendMessageOne(String message)
{
    selectLineOne();
Serial1.write(message);
delay(1000);
lcdON();
}
void sendMessageTwo(String message)
{
selectLineTwo();
Serial1.write(message);
delay(1000);
lcdON();
}
void lcdON(void){
Serial1.write(0x7C);   //command flag for backlight stuff
Serial1.write(157);     //light level for ON.
delay(10);
}
void lcdOFF(void){
Serial1.write(0x7C);   //command flag for backlight stuff
Serial1.write(128);     //light level for off.
delay(10);
}
void lcdCLEAR(void){
Serial1.write(254); // move cursor to beginning of first line
Serial1.write(128);
Serial1.write(”                “); // clear display
Serial1.write(”                “);
}
void selectLineOne(void){  //puts the cursor at line 0 char 0.
Serial1.write(254);   //command flag
Serial1.write(128);    //position
delay(10);
Serial1.write(”                “);
Serial1.write(254);   //command flag
Serial1.write(128);    //position
delay(10);
}
void selectLineTwo(void){  //puts the cursor at line 0 char 0.
Serial1.write(254);   //command flag
Serial1.write(192);    //position
delay(10);
Serial1.write(”                “);
Serial1.write(254);   //command flag
Serial1.write(192);    //position
delay(10);
}
void sendUpdate(const char * theItem, const char * command) {
request.hostname = “10.0.1.9”;  //this should be the address of your openHAB server
request.port = 8080;
request.path = theItem;
request.body = command;
http.post(request, response, headers);
Particle.publish(“log”, response.body);
}
////// END OF CODE FOR GARAGE DOOR OPENERS

Now push this code to the core and watch the LCD, you should now have a working App, with status of your garage doors.
So, next up, the voice control.  Head over to IFTTT and sign up/login to your account.  You will need to add the Google Assistant and the Particle services to IFTTT.  Then simple create a new rule using “If Google Assistant” add a phrase to open your door,  I made mine something that I would not accidentally say “would you please open my garage” and then the “then” section should be Particle in which you can select the open door function (if you have more than one garage you should have more than one function).
Now repeat for the close functions.
Thats it, you just connected Google Assistant to Particle and you should be able to say “OK Google, can you please open my garage” and presto!
Now, if you uncomment the code in the Particle site you should be able to send the status to openHAB if you are using that, as well you can create a button in openHAB to open/close the garage door manually.  Or you could even create an automatic rule to close the door in case you forget (but be careful with this one).
Let me know if this works for you, there are many changes and variations to this, but for me this has been reliable and up and running for over a year.  The Particle Cores are great and once you do this you will see a huge potential to use them in other projects!  (I have a neopixel LED changing project I will share soon!)
Getting this running in openHAB
So once you have your garage up and running with the particle core that is technically all you need, but you may want to add the control / automation side via openHAB.  While there are a number of ways to do this, I will outline what I did here.
SMLXL

First, install the Garadget Binding, this should be under the Paper UI –> Addons –> Bindings.  Once installed go into Configuration –> Bindings –> Garadget — >Configure and enter your username and password for you Particle Core website. This is how openHAB will communicate with your particle photon.
SMLXL

Now, to add control.  In your openHAB items file, add the following (edit for your needs).  The number item is to store the state (open or closed 1 or 0)  and the string is what command we will send/receive to Particle!
//Garage Doors
Number contact_BillGarageDoor  “Bill’s Garage Door is [MAP(en.map):%s]”
String billGarageControl “Bill’s Garage Control” { garadget=”>[2e0034000747343337373738#OperateDoor],<[2e0034000747343337373738#billStatus]” }

Finally you can add this to your sitemap file to create a group with status (Text item) and buttons to control (Switch item)
    Text item=contact_BillGarageDoor icon=”garagedoor” {
Switch item=billGarageControl icon=”garagedoor” mappings=[“OPEN2″=”OPEN”,”CLOSE2″=”CLOSE”]
}
SMLXL

SMXLL

Have fun! Let me know in the comments any questions, or comments!