Completed Garage Door and Web Interface - Schematic and Code included

So, this is my first post.

I originally got the spark because I wanted to control my garage door. I had been doing it with a Raspberry Pi, but that thing was a power hog. Plus, with the cloud-aspect of the core, things got a lot easier!

I saw the post here:

Which is a great start! But, I ran into a few problems with just using Tinker.
I’d like to share those with you guys. Suggestions are of course welcome!

If you don’t want to read about the process I had, just scroll down to the end to find the schematic, spark code and html/css/js code.


Problem 1)
One of the biggest issues is that depending on how you set up your relay (Normally open / normally closed), the garage door will activate when the core either powers on, or powers off. This is a pretty major security issue if it were to open on a power off situation (say your core dies, or there’s a glitch or something – obviously if power to the house goes out, then it’s not a huge problem because the garage door won’t be powered either :)) . If the garage activates when the core is powered on, well, then it’ll be difficult to ever update your code (assuming you move away from just using Tinker).

Solution 1)
It’d be pretty great if the core would allow you to load code RIGHT at power on, but unfortunately (at least from what I’ve found), it first must connect to the internet before it loads anything. Therefore, you can’t just set your relay HIGH/LOW (depending on how you set it up) at the start so that it doesn’t activate on power on.

So, what I did was use TWO relays that work opposite of each other and bridged them. When the core powers on, the relays are set LOW by default (which is a normally open position). So, what I did was set my first relay to be normally closed and my second relay to be normally open. So, what happens is that the relay chain has an open circuit when both relays are set LOW (which is default on power on). But, when I set the first relay HIGH, it switches from normally open, to closed and thus, completes the circuit, and sends the pulse to the garage door.

This way, no matter what the power situation of the core is, the garage door will never get a pulse unless you WANT it to get one.


Problem 2)
I wanted to be able to monitor whether or not my door was open or closed remotely!

Solution 2)
Reed switch. I put a reed switch on D6, set it to INPUT (which sets the pin HIGH, outputting the 3.3 v). Therefore, I used a normally open switch connected between D6 and GND. I have it set to where if the door is closed, the reed switch is NOT with it’s pair, so therefore, the D6 remains HIGH. When the door is open, the reed pairs meet up, the reed switch becomes “CLOSED” and sends D6 to GND, which then returns a “LOW”.


Problem 3)
As I stated in Problem 1 above, when relay 1 goes HIGH, it completes the circuit to the garage door and simulates pressing the button of the garage door. The issue is, when you press the button, you don’t press and HOLD it, you would press it and then release it!

Solution 3)
So, I wanted to simulate that. Therefore, I wrote a simple function for my core that would send relay 1 HIGH for a second, then bring it back LOW.

int relayPulse(String command){
    digitalWrite(relay, HIGH);
    delay(1000);
    digitalWrite(relay, LOW);
    return 1;
}

Problem 4)
This brings me to the last problem I faced. Because I was no longer just using Tinker, I had to be able to control the core outside of the phone app. I wanted to be able to remotely open and close it AND see if it was open or closed!

Solution 4)
Make a web app! Because the core is on the cloud and has API access, this wasn’t too bad to implement.
I had to implement another function in order to check the status of this door by seeing if the reed switch (problem 2) was HIGH or LOW. I threw in there a “neither HIGH nor LOW” which would indicate a disconnected core!

int statusCheck(String command){
    int status = digitalRead(reed);
    if(status == HIGH){
        return 1;
    }
    else if(status == LOW){
        return 0;
    }
    else{
        return -1;
    }
}

As for the webapp. I used a hosting site called https://www.bitballoon.com . It’s free, has the ability to make a site LOCKED! via a password you choose (which is nice so you don’t have anyone else opening or closing your garage door :slight_smile: ) and is drag and drop! So, you could just edit the “JS” code that I supplied, and drag that folder into bitballoon and you’re done!

The JS does a push request by calling the function “relayPulse” when you push the button on the webapp. It then does a another push request every X seconds (defined in the variable ‘status_check_time’) that runs the function “statusCheck” and returns a 1 if the garage is closed, a 0 if it’s open and a -1 if there is no response (indicating a disconnect). The webpage automatically updates every X seconds and displays the appropriate status on the page. You can also refresh, if you get impatient.


PROJECT FILES
All right! Sorry for the long post, but that’s it. Here are the files of my project:

  • Schematic:

    In this case, the switch on the left is the reed, and the button on the right is the garage door button (or you can wire it right up to the garage opener itself. Just make sure to uplug everything before working with these wires!!)

I didn’t have a good relay image to use in fritzing, but here is how I wired up in case the image above isn’t descriptive enough:

  • Core Code

I have relay1 set to D0 and the second relay on D3, but you can do whatever you want. Just remember that D2 can’t handle more than 3.3v, not that that SHOULD be an issue here. I just avoided it to be safe.

The relay is on pin D6, but again, do whatever you want.

int relay = D0;
int delayRelay = D3;
int reed = D6;

int relayPulse(String command){
    digitalWrite(relay, HIGH);
    delay(1000);
    digitalWrite(relay, LOW);
    return 1;
}
int statusCheck(String command){
    int status = digitalRead(reed);
    if(status == HIGH){
        return 1;
    }
    else if(status == LOW){
        return 0;
    }
    else{
        return -1;
    }
}
void setup() {
    Spark.function("relay", relayPulse);
    Spark.function("status", statusCheck);
    
    pinMode(relay, OUTPUT);
    pinMode(delayRelay, OUTPUT);
    pinMode(reed, INPUT);    
    
    digitalWrite(relay, LOW);
    digitalWrite(delayRelay, LOW);


}

void loop() {

}

Github page:

12 Likes

Awesome! Cmon…we need some pictures and video of the Garage door :smiley:

Wow… codepen.io looking cool…

1 Like

No doubt! This might replace GitHub gists for me!

I've thought about a garage door project, but I didn't much use of it personally. I don't think my wife or I have ever accidentally left the garage door open, but you never know. Regardless, I've bookmarked and starred this one for later. This is a GREAT post with really good write-up and illustrations!

@austinpe,

Thank you very much for sharing your Garage Door Project; Very Nice Work !

I’ve definitely left my garage door open a couple times, but luckily I’ll take the dog out late at night and catch it before I go to bed. Still though, it has been open in the dark a lot longer than I would have wanted. Definitely something I planned to solve with bluetooth one day.

I really like the web app, and clean and simple CSS styling :slight_smile: I would like to propose a similar styling for a Spark.css and Spark.js Framework that can be used to create Spark Core enabled web apps, like Twitter Bootstrap… but more Sparkcentric for things like mobile phones and tablets. I’m not web guru enough to make it happen, but I think it’s a good idea. cc: @zach

1 Like

Interesting, I am working on almost the same project. The difference is that I wrote a Asp.Net app that allows a password to be entered to open my door via the web. The good part about using the Asp.Net is that the token and access code is secure since it isn’t in the html markup, it is in the compiled DLL.

2 Likes

Hi,
I am new to spark core and I salute you guys you have done a great job in IOT. I like garage door control project. That is great effort. I m also working on the same thing, but with a manual over ride. If any anyone at home open door and it is not opened by web app or android app, how I can get real status of the garage door. In early web app uses post function to retrieve the status of the door periodically. in this scenario Core and web app continuously send and receiving data. if at the start of the app is retrieve data from but post function as digital read, either Open or Close. after that Core also publish it state itself in case of any change. In this scenario no extra sending and receiving in Core Cloud and web app. I need your help for the this.

Core Coding

int tinkerDigitalRead(String pin);
int tinkerDigitalWrite(String command);
static bool pressed = false;

void setup()
{
 

 
	Spark.function("digitalread", tinkerDigitalRead);
	Spark.function("digitalwrite", tinkerDigitalWrite);
	
	 pinMode(A7, INPUT);

 

}


 
void loop()
{
    
     if(digitalRead(A7) == HIGH && pressed == false)
    {
        Spark.publish("garage_state", "OPEN");
        pressed = true;
         
 
        delay(500);
    }
    else if(digitalRead(A7) == LOW && pressed == true)
    {
        Spark.publish("garage_state", "CLOSED");
        pressed = false;
    }

	 
}

 
int tinkerDigitalRead(String pin)
{
 
	int pinNumber = pin.charAt(1) - '0';
	 
	if (pinNumber< 0 || pinNumber >7) return -1;


	if(pin.startsWith("D"))
	{
		pinMode(pinNumber, INPUT_PULLDOWN);
		return digitalRead(pinNumber);
	}
	else if (pin.startsWith("A"))
	{
		pinMode(pinNumber+10, INPUT_PULLDOWN);
		return digitalRead(pinNumber+10);
	}
	return -2;
}
int tinkerDigitalWrite(String command)
{
	bool value = 0;
	 
	int pinNumber = command.charAt(1) - '0';
 
	if (pinNumber< 0 || pinNumber >7) return -1;


	if(command.substring(3,7) == "HIGH") value = 1;
	else if(command.substring(3,6) == "LOW") value = 0;
	else return -2;


	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		digitalWrite(pinNumber, value);
		return 1;
	}
	else if(command.startsWith("A"))
	{
		pinMode(pinNumber+10, OUTPUT);
		digitalWrite(pinNumber+10, value);
		return 1;
	}
	else return -3;
}
indent preformatted text by 4 spaces

So, instead of polling the Core, you want the Core to send out a message if the State is changed? Using the Spark.publish should work. I’m not sure why you’re using the Tinker firmware in your code, unless you absolutely want to use the Tinker app. Using the Code provided by @austinpe, combined with some stuff from one of my little projects, I think I’ve managed to create something that might work.

New Core Code:

#define relay D7
#define delayRelay D3
#define reed D6
int Interval = 1000;
char status;
char tempState;

void setup() {
    Spark.function("relay", relayPulse); 
    Spark.function("state", state);

    Serial.begin(9600);
    
    pinMode(relay, OUTPUT);
    pinMode(delayRelay, OUTPUT);
    pinMode(reed, INPUT);    

    digitalWrite(relay, LOW);
    digitalWrite(delayRelay, LOW);

	status = digitalRead(reed); //Check the Start-up status with the Reed contact.
	Spark.publish("garage_state", String(status)); //Publish start-up state.
	Serial.println("startup status:" + status);
};

void loop() {
    //Comment this section out if you haven't got a sensor attached.
    //otherwise it will spit out random values, which isn't what we need.
	tempState = digitalRead(reed); //Check the current state
	if (status != tempState){ //Compare current state to status.
		status = tempState; //Make tempState the new status
		Spark.publish("garage_state", String(status)); //Publish the status.
		delay(Interval); //Delay Interval millisecond, as to not to overload the server.
	}
};

//For testing purposes only, so you can change the state with a Function.
//Make a REST request and instert the state in the argument, either HIGH
//or LOW. You'll notice the changes on the webpage. I suggest using this
//for testing: http://suda.github.io/spark-web-interface/ it's really 
//easy to use. Simply click the "execute" button at the "state" function
//and then enter either HIGH or LOW. Should work...
int state(String command){
	String state = String(command);
	Serial.println("Command:"+command);
	Serial.println("state:"+state);
	Spark.publish("garage_state", String(state)); //Publish the status.
} 

int relayPulse(String command){
    digitalWrite(relay, HIGH);
    delay(2000);
    digitalWrite(relay, LOW);
    return 1;
};

I’ve used the original HTML/CSS file, so you will still need those. I’ve edited the JavaScript though.
JavaScript:

var deviceID    = "1234567890123456789";	
var accessToken = "1234567890asdfghjkl1234567890";	

/*EDIT ABOVE VARIABLES*/
var status = "HIGH";
var baseURL = "https://api.spark.io/v1/devices/";
//Open Eventstream.
var eventSource = new EventSource(baseURL + deviceID + "/events/?access_token=" + accessToken); 

$(document).ready(function () {
    "use strict";
    console.log(status);
    //Hide the next three buttons until we know we've got a connection.
    $('.status_closed').css('display', 'none');
    $('.status_open').css('display', 'none');
    $('.button').css('display', 'none');


$(".button").click(function () {
    "use strict";
    $.ajax({
        url: baseURL + deviceID + "/relay",
        async: false,
        type: "POST",
        data: {access_token: accessToken}
    });
});

eventSource.addEventListener('garage_state', function(e) {	//Listen to SSE
	var status = (JSON.parse(e.data)).data;	
	console.log("status" + status);
	
	//Enable Push button now that we know we've got a connection.
	$('.button').css('display', 'block'); 
  
	
  if (status != "HIGH" && status != "LOW"){ 
    $('.status_none').css('display', 'block'); 
    //Disable "no connection" message.
  }
  else if (status == "HIGH") {
    $('.status_open').css('display', 'block'); 
    $('.status_closed').css('display', 'none');
    $('.status_none').css('display', 'none');
    //Enable "open" message.
  } 
  else if (status == "LOW") {
    $('.status_closed').css('display', 'block'); 
    $('.status_open').css('display', 'none');
    $('.status_none').css('display', 'none');
    //Enable "closed" message.
  } 
}, false);


});

Since I haven’t got the required components, I haven’t been able to test this. Might at least be useful as some inspiration as to how it could be done.

Hope it helps!


Some improvements are made to the code, as things weren’t working properly. Also, there is some sort of testing function included.

1 Like

Thanks you so much brother. sorry I load the code to core and edit original web app with this JQuery it still not working you help needed.

bro code is working now but when status change at D6, there is not change in index no button show for status

Could you post your (working) code, that’ll be easier to figure out what’s wrong?

var key = "kjshdkjashjkds";


var device_id = "kkdsckjasjkd";

 

/*EDIT ABOVE VARIABLES*/
status = "NONE";
var baseURL = "https://api.spark.io/v1/devices/";
//Open Eventstream.
var eventSource = new EventSource(baseURL + deviceID + "/events/?access_token=" + accessToken); 

$(document).ready(function () {
    "use strict";
    console.log(status);
    //Hide the next three buttons until we know we've got a connection.
    $('.status_closed').css('display', 'none');
    $('.status_open').css('display', 'none');
    $('.button').css('display', 'none');
});

$(".button").click(function () {
    "use strict";
    $.ajax({
        url: baseURL + deviceID + "/relay",
        async: false,
        type: "POST",
        data: {access_token: accessToken}
    });
});

eventSource.addEventListener('garage_state', function(e) {	//Listen to SSE
	"use strict";
	//Read the SSE data.
	status = e.data; 
	//Enable Push button now that we know we've got a connection.
	$('.button').css('display', 'block'); 
	//Compare the state
	if (status != "NONE"){ 
		$('.status_none').css('display', 'none'); 
		//Disable "no connection" message.
	};
	if (status == "HIGH") {
		$('.status_open').css('display', 'block'); 
		//Enable "open" message.
	} 
	else if (status == "LOW") {
		$('.status_closed').css('display', 'block'); 
		//Enable "closed" message.
	} 
}, false);

    indent preformatted text by 4 spaces
#define relay D7
#define delayRelay D3
#define reed D6
int Interval = 1000;
char status;
char tempState;

void setup() {
    Spark.function("relay", relayPulse); 

    pinMode(relay, OUTPUT);
    pinMode(delayRelay, OUTPUT);
    pinMode(reed, INPUT);    

    digitalWrite(relay, LOW);
    digitalWrite(delayRelay, LOW);

	status = digitalRead(reed); //Check the Start-up status with the Reed contact.
	Spark.publish("garage_state", String(status)); //Publish start-up state.
};

void loop() {
	tempState = digitalRead(reed); //Check the current state
	if (status != tempState){ //Compare current state to status.
		status = tempState; //Make tempState the new status
		Spark.publish("garage_state", String(status)); //Publish the status.
		delay(Interval); //Delay Interval millisecond, as to not to overload the server.
	}
};

int relayPulse(String command){
    digitalWrite(relay, HIGH);
    delay(2000);
    digitalWrite(relay, LOW);
    return 1;
};

brother I m working with same code with original web app just change js.

brother code is working good. Thank you so much. I m so happy for your help.

Glad to hear it’s working. Have you just used the code I provided above, or have you made any changes (other than the credentials)? If so, would you mind posting the changed code you have used, so that other may use it in the future? If not, they can use my code I guess (which I’m sure can be improved quite a bit by someone more knowledgeable).

you code work as it is. just change credentials. I m so thankful to you

1 Like

How did you power your spark core and relay board? I am doing a similar project but I can’t decide what to use as a power supply. My garage door opener has a 24vdc output on the back, could I use that and use a dc-dc converter to convert it down to 5v?

My garage door is powered by a nearby normal mains wall socket, so I just used a normal wall plug to usb thing (I know, very technical) to power the spark.

I did a very similar setup with the two relays. It works great from the web interface, but now my regular remotes only work from within the garage. I’ve moved the USB supply 10ft away and the core 5ft away from the garage opener/antenna. The range of the regular remotes didn’t improve either, but goes back to normal when the spark is fully removed.

Any ideas what could be causing this interference?