Build a Web-based Garage Door Controller with LaunchPad
2017-01-04 | By All About Circuits
License: See Original Project Launchpad
Courtesy of All About Circuits
Open and close your garage door with an Android device!
Overview
I was motivated to find a new garage opener solution so I’d have less things to carry around, plus most openers just don't work very well. In this project, you’ll learn how to use a CC3200 to connect with an existing garage door opener. The CC3200 acts as a TCP server which sends information about the state of the garage door to any network capable device. It can also open and close the garage door by sending a TCP message. We’ll create an Android application to act as a garage door remote.
Requirements
- CC3200 Demo board
- Latest firmware for Energia support.
- Energia - an IDE from TI based on processing, similar to Arduino’s IDE
- IDE used in this article: v. 0101E00016
- Wire to connect with a garage door opener
- Optional: Android device to use as a garage remote control
- Used in article: Android Lollipop
- Optional: Android Studio for making an Android app
Hardware Setup
My garage door opener is a Chamberlain brand. The garage door motor’s rear has four wires that go into it for the sensors and the hard-wired switch. I used a DMM to find out which wire is connected to the hard-wired switch. The DMM probes the voltage while I press the switch. I assumed that the white wires were ground. The blue wire is about 5-6V and didn't react when I pressed the garage switch. I found that the wire potential of red to white is normally about 15.8V when the switch isn’t pressed, and drops to 0V when the switch is pressed. This makes it easy to use an NPN or NMOS transistor to simulate a button press from the CC3200.
Reading the garage door status
This is a little trickier because there is no way to tell from any of the connections on the motor. Fortunately, I thought of a few ways to tell if the door is open or closed.
1. Use a single limit switch at the top of the garage door by the motor. If the limit switch is pressed, the door is open. If the switch is closed, the door most likely is closed. The door could be stuck halfway open and the CC3200 would not know.
2. You can use two limit switches at the top and bottom of the garage door. The switch that is pressed will determine if the door is open or closed. If both switches are open, the door is moving or stuck half-way.
3. Use a distance sensor in-line with the door rail. The distance becomes greater as the door closes. This will give you fine resolution into the exact state of the door.
I used option 1 to keep things simple. I mounted the switch on the door bracket near the rear so it will activate when the door is fully open.
Connection Diagram
- The switch is pulled-up to VCC to create an active-low connection to the GPIO input.
- A GPIO output is interfaced to the motor’s red wire through an NPN transistor in order to isolate the low-voltage CC3200 from the 15V switch voltage.
- The resistor value isn’t important, you can use whatever you have laying around, ~500-50 kohm is reasonable. If you use an NMOS transistor instead, you won’t need a resistor at all.
Software
Embedded
Here’s what the Energia project code does:
1. Connects to the wifi network specified in the configuration variables.
2. Obtains an IP address from the router.
3. Opens a TCP server on the port specified in the settings
4. Waits for a client to connect.
5. When a client connects, it waits for a password and commands.
6. If the password is correct and the command matches a known command, an action will be performed.
7. The server will respond with the state of the garage depending on the command: activated, open, or closed.
#include
#include
#define SERVER_PORT 23
#define LIMIT_SW_PIN 2
#define MOTOR_SW_PIN 8
#define GRN_LED 10
#define RED_LED 29
#define YELLOW_LED 9
//configuration variables
char ssid[] = "ssid";
char password[] = "pass";
char garage_password[] = "mypass";
char command_activate[] = "Activate";
char command_status[] = "Status";
boolean alreadyConnected = false; // whether or not the client was connected previously
WiFiServer server(SERVER_PORT);
void setup() {
//debug serial port
Serial.begin(115200);
//interface pins
pinMode(GRN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(MOTOR_SW_PIN, OUTPUT);
pinMode(LIMIT_SW_PIN, INPUT_PULLUP);
digitalWrite(GRN_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(RED_LED, HIGH);
// attempt to connect to Wifi network:
Serial.print("Attempting to connect to Network named: ");
// print the network name (SSID);
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED) {
// print dots while we wait to connect
Serial.print(".");
delay(300);
}
Serial.println("\nYou're connected to the network");
Serial.println("Waiting for an ip address");
while (WiFi.localIP() == INADDR_NONE) {
// print dots while we wait for an ip addresss
Serial.print(".");
delay(300);
}
Serial.println("\nIP Address obtained");
// you're connected now, so print out the status:
printWifiStatus();
// start the server:
server.begin();
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, HIGH);
}
#define CLIENT_BUFF_SIZE 100
char client_in_buffer[CLIENT_BUFF_SIZE];
uint8_t idx=0;
void loop() {
// wait for a new client:
WiFiClient client = server.available();
if (client) {
digitalWrite(YELLOW_LED, LOW);
if (!alreadyConnected) {
// clead out the input buffer:
client.flush();
Serial.println("Client connected");
client.println("Garage connected!");
alreadyConnected = true;
digitalWrite(GRN_LED, HIGH);
}
if (client.available() > 0) {
char thisChar = client.read();
Serial.write(thisChar);
if(thisChar == '\n'){
if(strncmp(client_in_buffer,garage_password,strlen(garage_password)) == 0){
Serial.println("passwords match");
if(strncmp(client_in_buffer strlen(garage_password) 1,command_activate,strlen(command_activate)) == 0){
Serial.println("Activate");
client.println("Garage activated");
digitalWrite(MOTOR_SW_PIN, HIGH);
delay(200);
digitalWrite(MOTOR_SW_PIN, LOW);
}
if(strncmp(client_in_buffer strlen(garage_password) 1,command_status,strlen(command_status)) == 0){
Serial.println("Status");
if(digitalRead(LIMIT_SW_PIN) == HIGH) client.println("Garage is open");
else client.println("Garage is closed");
}
}
memset(client_in_buffer,0,CLIENT_BUFF_SIZE);
idx=0;
}
else{
client_in_buffer[idx]=thisChar;
idx ;
if(idx>=CLIENT_BUFF_SIZE){
idx=0;
memset(client_in_buffer,0,CLIENT_BUFF_SIZE);
}
}
}
}
else{
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GRN_LED, LOW);
alreadyConnected = false;
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Android Application
The Android application opens a connection to the server. You'll have to modify the IP address you want to use. If you want to connect from outside your local network, you'll have to forward the port through the router to the IP address. You'll have more security if you only allow connections inside the local network. The application is just a couple buttons and some status indication. The activate button triggers the motor button for 200ms, and the status button reads the limit switch.
package com.example.travis.garagecontroller;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.os.Bundle;
import android.view.View;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.os.Handler;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Socket socket;
Handler updateConversationHandler;
private static final int SERVER_PORT = 23;
private static final String SERVER_IP = "192.168.1.144";
private static final String PASSWORD = "mypass";
private TextView t_garage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t_garage = (TextView) findViewById(R.id.t_garage);
updateConversationHandler = new Handler();
new Thread(new ClientThread()).start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onClick_activate(View view) {
try {
String packet = PASSWORD ",Activate";
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(packet);
//get data back from server
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch (UnknownHostException e) {
t_garage.setText("Can't find garage");
}
catch (IOException e) {
t_garage.setText("Comm error connect");
}
catch (Exception e) {
t_garage.setText("Can't find garage");
}
}
public void onClick_status(View view) {
try {
String packet = PASSWORD ",Status";
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(packet);
//get data back from server
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
catch (UnknownHostException e) {
t_garage.setText("Can't find garage");
}
catch (IOException e) {
t_garage.setText("Comm error connect");
}
catch (Exception e) {
t_garage.setText("Can't find garage");
}
}
class ClientThread implements Runnable {
@Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVER_PORT);
SeverResponseThread serverThread = new SeverResponseThread(socket);
new Thread(serverThread).start();
} catch (UnknownHostException e1) {
t_garage.setText("Can't find garage");
} catch (IOException e1) {
t_garage.setText("Comm error connect");
}
}
}
class SeverResponseThread implements Runnable {
private Socket clientSocket;
private BufferedReader input;
public SeverResponseThread(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
}
catch (IOException e) {
t_garage.setText("Comm error write");
}
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
String read = input.readLine();
updateConversationHandler.post(new updateTextThread(read));
}
catch (IOException e) {
t_garage.setText("Comm error read");
}
}
}
}
class updateTextThread implements Runnable {
private String server_response;
public updateTextThread(String str) {
this.server_response = str;
}
@Override
public void run() {
t_garage.setText(server_response);
}
}
}
APK
The IP address is hard-coded to 192.168.1.144 and the port is 23.
You can download all of the codes for this project on All About Circuits.
Testing the Door
In the video below I control the garage door by pressing the Activate button on my phone.
Conclusion
This project serves as a guideline to control many things using TCP connections with the CC3200. You don't necessarily have to use an Android device. Any network connected device capable of TCP can be used as a garage door remote.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum