Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
| |||||
| ||||||
![]() |
|
This is a bit more advanced than your average garage door automation story. In this project, we include sensors for status feedback, integration with Amazon Alexa and IFTTT. We get notifications to your phone, logging to ubidots and Atomiot.
Particle photon, relay shield and non contact inductive proximity sensors mounted above the door.
The hardware is rather simple. A particle Photon relay shield, and DHT11 sensor for temp/humidity logging, two NO inductive 5mm sensors for door status detection. The relay shield closes the relay contacts for 250ms to trigger the door button.
The code includes the schematic.
Most implementations do not know the door status and if you tell the door to open twice, you may end up closing the door before you get it open.
This is circumvented by adding position sensors that can detect if the door is fully open or closed. The logic prevents actuation if the door is in "limbo"
The mobicle.io interface works well for monitoring variables and triggering outputs. "Limbo" indicates the door is somewhere between up and down.
Amazon Echo integration allows for remote door opening and closing via voice command.
A Timer fires every 10 seconds and updates an event called RSSI with the signal strength. This is handy for keeping tabs on my particles and their connections.
Updated code
Arduino// This #include statement was automatically added by the Particle IDE.
#include "PietteTech_DHT/PietteTech_DHT.h"
/* +-----+
* +----------| USB |----------+
* | +-----+ * |
* | [ ] VIN 3V3 [ ] |
* | [ ] GND RST [ ] |
* | [ ] TX VBAT [ ] |
* | [ ] RX [S] [R] GND [ ] |
* | [ ] WKP D7 [ ] |
* | [ ] DAC +-------+ D6 [ ] |
* Opensensor -->| [*] A5 | * | D5 [ ] |
* Closedsensor -->| [*] A4 |Photon | D4 [*] |<-- DHT21 AM2301 Sensor
* | [*] A3 | | D3 [ ] |
* | [ ] A2 +-------+ D2 [ ] |
* | [ ] A1 D1 [ ] |
* Analog0 -->| [ ] A0 D0 [*] |<-- Garage Door Switch Relay
* | |
* \ [] [______] /
* \_______________________/
*
*
*/
const String key = "YOUR_KEY_GOES_HERE"; //write key
//--------------Begin DHT------------------
// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain
#define DHTPIN D4 // what pin we're connected to
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302)
#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
//declaration
void dht_wrapper(); // must be declared before the lib initialization
// Lib instantiate
PietteTech_DHT DHT(DHTPIN, DHTTYPE, dht_wrapper);
// globals
unsigned int DHTnextSampleTime; // Next time we want to start sample
bool bDHTstarted; // flag to indicate we started acquisition
int n; // counter
//---------------End DHT-------------------
unsigned int particleinterval = 100;
unsigned int particlefuncinterval = 10000;
unsigned int ubiinterval = 15000;
unsigned int dhtinterval = 5000;
int garageDoorFunction(String command);
int rssival = 0;
int doorstatus = 1;
int doorcounter = 0;
int closedsensorpin = A4;
int opensensorpin = A5;
const int open = 0;
const int closed = 1;
// Define the pins we're going to call pinMode on
int led = D6; // You'll need to wire an LED to this one to see it blink.
int led2 = D7; // This one is the built-in tiny one to the right of the USB jack
double analog1 = 0;
double analog2 = 0;
double analog1raw = 0;
double analog2raw = 0;
double garagetemp = 70;
double readtemp = 70;
double garagehumid = 0;
char publishString[40];
char doorpublishstring[40];
char doorstatusstring[40];
SYSTEM_MODE(SEMI_AUTOMATIC); //allows for control of Spark.connect() and Spark.process()
// This routine runs only once upon reset
Timer task50ms(50, dodoor);
Timer task10sec(10000,dopublish);
Timer task15sec(15000,updateThingspeak);
Timer taskdht(5000, readdht);
void setup() {
task50ms.start();
task10sec.start();
task15sec.start();
taskdht.start();
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
Serial1.begin(230400); // open serial over USB
//ip = {"IP:", WiFi.localIP()};
//String ip = WiFi.localIP();
Particle.function("GarageDoor", garageDoorFunction);
Particle.variable("analog1", &analog1, DOUBLE); //variables update automatically. Max 4 per particle
Particle.variable("doorcounter", doorcounter);
//Particle.variable("doorstatus", &doorstatus, INT); //variables are viewable on mobicle
Particle.variable("doorstatus", doorstatusstring, STRING); //variables are viewable on mobicle
Particle.variable("garagetemp", &garagetemp, DOUBLE); //variables are viewable on mobicle
Particle.variable("garagehumid", &garagehumid, DOUBLE); //variables are viewable on mobicle
pinMode(opensensorpin, INPUT_PULLUP);
pinMode(closedsensorpin, INPUT_PULLUP);
pinMode(D0, OUTPUT);
//Do DHTINIT
pinMode(DHTPIN, INPUT_PULLUP); //sensor needs pullup resistor.
//End DHTINIT
//-------------Begin UBIDOTS---------------
// request.hostname = "things.ubidots.com";
// request.port = 80;
// request.path = "/api/v1.6/variables/"VARIABLE_ID"/values?token="TOKEN;
//--------------End UBIDOTS----------------
}
// This wrapper is in charge of calling
// mus be defined like this for the lib work
void dht_wrapper() {
DHT.isrCallback();
}
void loop() {
if (Particle.connected() == false){
Particle.connect();
}
analog1raw = analogRead(A0);
analog1 = (analog1 *63 +analog1raw *3.3/4095)/64;
analog2raw = analogRead(A1);
analog2 = (analog2 *63 +analog2raw *3.3/4095)/64;
doDht();
}
void dopublish(){
rssival = WiFi.RSSI();
sprintf(publishString,"%d",rssival);
Particle.publish("RSSI",publishString);
}
void readdht() {
if (!bDHTstarted) { // start the sample
Serial.print("\n");
Serial.print(n);
Serial.print(": Retrieving information from sensor: ");
DHT.acquire();
bDHTstarted = true;
}
}
void dodoor() {
digitalWrite(led, !digitalRead(led)); // Turn ON the LED pins
digitalWrite(led2, !digitalRead(led2));
int opensense = digitalRead(opensensorpin);
int closedsense = digitalRead(closedsensorpin);
if (opensense == LOW) // high,0 = open door, low,1 = closed door
{
if (doorstatus == closed) //check the previous state, if closed, then generate an event
{
strcpy(doorpublishstring, "Open");
Particle.publish("GARAGEDOOR",doorpublishstring);
doorcounter++;
}
doorstatus = open;
strcpy(doorstatusstring, "Open"); //update particle variable.
}
else if (closedsense == LOW)
{
if (doorstatus == open) //check the previous state, if closed, then generate an event
{
strcpy(doorpublishstring, "Closed");
Particle.publish("GARAGEDOOR",doorpublishstring);
doorcounter++;
}
doorstatus = closed;
strcpy(doorstatusstring, "Closed"); //update particle variable.
}
else
{ //if door is neither closed or open, it is in "limbo"
strcpy(doorstatusstring, "Limbo"); //update particle variable.
}
}
/*******************************************************************************
* Function Name : garageDoor
* Description : based on door status, opens or closes the door
* Input : analog 5 - digital input.
* Output : digital 1 - relay to blip and open or close the door
* Return : Value of the pin (0 or 1) in INT type
Returns a negative number on failure
*******************************************************************************/
int garageDoorFunction(String sesame)
{
if(sesame.startsWith("C"))
{
if (doorstatus == open)
{
pinMode(0, OUTPUT);
digitalWrite(0, 1);
delay(250);
digitalWrite(0, 0);
return 1;
}
}
else if (sesame.startsWith("O"))
{
if (doorstatus == closed)
{
pinMode(0, OUTPUT);
digitalWrite(0, 1);
delay(250);
digitalWrite(0, 0);
return 2;
}
}
else if (sesame.startsWith("X"))
{
if (doorstatus == closed){
return 999;
} else
return 123;
}
return -1;
}
/*******************************************************************************
* Function Name : updateThingspeak
* Description : sends variables to ts
* Input : doorstatus global variables
* Output : sends data to ts
* Return : void
*******************************************************************************/
bool updateThingspeak()
{
//delay (2000);
static int count = 0;
Serial.println(count++);
int rssival = WiFi.RSSI();
//sprintf(publishString,"%d",rssival);
//bool success = Particle.publish("RSSI",publishString);
//sprintf(publishString, "%1.4f", checkbattery());
bool success = Particle.publish("thingSpeakWrite_All", +
"{ \"1\": \"" + String(rssival) + "\"," +
"\"2\": \"" + String(garagetemp) + "\"," +
"\"3\": \"" + String(garagehumid) + "\"," +
"\"4\": \"" + String(doorstatus) + "\"," +
"\"5\": \"" + String(doorcounter) + "\"," +
"\"6\": \"" + String(analog1) + "\"," +
"\"7\": \"" + String(analog2) + "\"," +
"\"k\": \"" + key + "\" }", 60, PRIVATE);
return success; //if sent, then turn of the send flag, otherwise let it try again.
}
/*******************************************************************************
* Function Name : getDht
* Description : gets temp and humidity
* Input :
* Output :
* Return : void
*******************************************************************************/
void doDht() {
if (!DHT.acquiring()) { // has sample completed?
// get DHT status
int result = DHT.getStatus();
Serial.print("Read DHT sensor: ");
switch (result) {
case DHTLIB_OK:
Serial.println("OK");
garagehumid = DHT.getHumidity();
// Read temperature as Celsius
//garagetemp = DHT.getCelsius();
// Read temperature as Farenheit
garagetemp = DHT.getFahrenheit();
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.println("Error\n\r\tChecksum error");
break;
case DHTLIB_ERROR_ISR_TIMEOUT:
Serial.println("Error\n\r\tISR time out error");
break;
case DHTLIB_ERROR_RESPONSE_TIMEOUT:
Serial.println("Error\n\r\tResponse time out error");
break;
case DHTLIB_ERROR_DATA_TIMEOUT:
Serial.println("Error\n\r\tData time out error");
break;
case DHTLIB_ERROR_ACQUIRING:
Serial.println("Error\n\r\tAcquiring");
break;
case DHTLIB_ERROR_DELTA:
Serial.println("Error\n\r\tDelta time to small");
break;
case DHTLIB_ERROR_NOTSTARTED:
Serial.println("Error\n\r\tNot started");
break;
default:
Serial.println("Unknown error");
break;
}
/*
Serial.print("Humidity (%): ");
Serial.println(DHT.getHumidity(), 2);
Serial.print("Temperature (oC): ");
Serial.println(DHT.getCelsius(), 2);
Serial.print("Temperature (oF): ");
Serial.println(DHT.getFahrenheit(), 2);
Serial.print("Temperature (K): ");
Serial.println(DHT.getKelvin(), 2);
Serial.print("Dew Point (oC): ");
Serial.println(DHT.getDewPoint());
Serial.print("Dew Point Slow (oC): ");
Serial.println(DHT.getDewPointSlow());
*/
n++; // increment counter
bDHTstarted = false; // reset the sample flag so we can take another
}
//Serial.println(Time.timeStr());
}
//Garage Door control code 4-10-16
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_DHT/Adafruit_DHT.h"
/* +-----+
* +----------| USB |----------+
* | +-----+ * |
* | [ ] VIN 3V3 [ ] |
* | [ ] GND RST [ ] |
* | [ ] TX VBAT [ ] |
* | [ ] RX [S] [R] GND [ ] |
* | [ ] WKP D7 [ ] |
* | [ ] DAC +-------+ D6 [ ] |
* Opensensor -->| [*] A5 | * | D5 [ ] |
* Closedsensor -->| [*] A4 |Photon | D4 [*] |<-- DHT21 AM2301 Sensor
* | [*] A3 | | D3 [ ] |
* | [ ] A2 +-------+ D2 [ ] |
* | [ ] A1 D1 [ ] |
* Analog0 -->| [ ] A0 D0 [*] |<-- Garage Door Switch Relay
* | |
* \ [] [______] /
* \_______________________/
*
*
*/
// This #include statement was automatically added by the Particle IDE.
#include "HttpClient/HttpClient.h"
#include "application.h"
// This #include statement was automatically added by the Spark IDE.
#include "elapsedMillis/elapsedMillis.h"
//-------------Begin UBIDOTS---------------
#define VARIABLE_ID_DOORSTATUS "ID HERE"
#define VARIABLE_ID_VOLTAGE "ID HERE"
#define TOKEN "TOKEN HERE"
HttpClient http;
int lightLevel = 0;
unsigned int nextTime = 0; // Next time to contact the server
// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
{ "Content-Type", "application/json" },
{ NULL, NULL } // NOTE: Always terminate headers will NULL
};
http_request_t request;
http_response_t response;
//--------------End UBIDOTS----------------
//--------------Begin DHT------------------
// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain
#define DHTPIN D4 // what pin we're connected to
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302)
#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
DHT dht(DHTPIN, DHTTYPE);
//---------------End DHT-------------------
unsigned int interval = 50;
unsigned int particleinterval = 100;
unsigned int particlefuncinterval = 10000;
unsigned int ubiinterval = 5000;
unsigned int dhtinterval = 30000;
int garageDoorFunction(String command);
int rssival = 0;
int doorstatus = 1;
int doorcounter = 0;
int closedsensorpin = A4;
int opensensorpin = A5;
const int open = 0;
const int closed = 1;
// Define the pins we're going to call pinMode on
int led = D6; // You'll need to wire an LED to this one to see it blink.
int led2 = D7; // This one is the built-in tiny one to the right of the USB jack
double analog1 = 0;
double analog1raw = 0;
double garagetemp = 0;
double garagehumid = 0;
elapsedMillis timeElapsed; //declare global if you don't want it reset every time loop runs
elapsedMillis timeElapsed2; //declare global if you don't want it reset every time loop runs
elapsedMillis timeElapsed3;
elapsedMillis timeElapsed4;
elapsedMillis timeElapsed5;
char publishString[40];
char doorpublishstring[40];
char doorstatusstring[40];
SYSTEM_MODE(SEMI_AUTOMATIC); //allows for control of Spark.connect() and Spark.process()
// This routine runs only once upon reset
void setup() {
timeElapsed = 0;
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(A0, INPUT);
Serial1.begin(230400); // open serial over USB
//ip = {"IP:", WiFi.localIP()};
//String ip = WiFi.localIP();
Particle.function("GarageDoor", garageDoorFunction);
Particle.variable("analog1", &analog1, DOUBLE); //variables update automatically. Max 4 per particle
Particle.variable("doorcounter", doorcounter);
//Particle.variable("doorstatus", &doorstatus, INT); //variables are viewable on mobicle
Particle.variable("doorstatus", doorstatusstring, STRING); //variables are viewable on mobicle
Particle.variable("garagetemp", &garagetemp, DOUBLE); //variables are viewable on mobicle
Particle.variable("garagehumid", &garagehumid, DOUBLE); //variables are viewable on mobicle
pinMode(opensensorpin, INPUT_PULLUP);
pinMode(closedsensorpin, INPUT_PULLUP);
pinMode(D0, OUTPUT);
//Do DHTINIT
pinMode(DHTPIN, INPUT_PULLUP); //sensor needs pullup resistor.
dht.begin();
//End DHTINIT
//-------------Begin UBIDOTS---------------
// request.hostname = "things.ubidots.com";
// request.port = 80;
// request.path = "/api/v1.6/variables/"VARIABLE_ID"/values?token="TOKEN;
//--------------End UBIDOTS----------------
}
void loop() {
if (Particle.connected() == false){
Particle.connect();
}
analog1raw = analogRead(A0);
analog1 = (analog1 *63 +analog1raw *3.3/4095)/64;
if (timeElapsed> interval)
{ //sets up interval timer and fires on interval
digitalWrite(led, !digitalRead(led)); // Turn ON the LED pins
digitalWrite(led2, !digitalRead(led2));
int opensense = digitalRead(opensensorpin);
int closedsense = digitalRead(closedsensorpin);
if (opensense == LOW) // high,0 = open door, low,1 = closed door
{
if (doorstatus == closed) //check the previous state, if closed, then generate an event
{
strcpy(doorpublishstring, "Open");
Particle.publish("GARAGEDOOR",doorpublishstring);
doorcounter++;
}
doorstatus = open;
strcpy(doorstatusstring, "Open"); //update particle variable.
}
else if (closedsense == LOW)
{
if (doorstatus == open) //check the previous state, if closed, then generate an event
{
strcpy(doorpublishstring, "Closed");
Particle.publish("GARAGEDOOR",doorpublishstring);
doorcounter++;
}
doorstatus = closed;
strcpy(doorstatusstring, "Closed"); //update particle variable.
}
else
{ //if door is neither closed or open, it is in "limbo"
strcpy(doorstatusstring, "Limbo"); //update particle variable.
}
timeElapsed = 0; //resets interval timer.
}
if (timeElapsed2>particleinterval) //Run code every .1 seconds
{
Particle.process(); //Process wifi events
}
if (timeElapsed3> particlefuncinterval) //Run code every 10 seconds
{
rssival = WiFi.RSSI();
sprintf(publishString,"%d",rssival);
Particle.publish("RSSI",publishString);
timeElapsed3 = 0; //reset interval timer
}
if (timeElapsed4> ubiinterval) //Run code every 10 seconds
{
updateUbidots();
timeElapsed4 = 0; //reset interval timer
}
if (timeElapsed5> dhtinterval) //Run code every 10 seconds
{
getDht();
timeElapsed5 = 0; //reset interval timer
}
//delay(2);
// delay(1000);
}
/*******************************************************************************
* Function Name : garageDoor
* Description : based on door status, opens or closes the door
* Input : analog 5 - digital input.
* Output : digital 1 - relay to blip and open or close the door
* Return : Value of the pin (0 or 1) in INT type
Returns a negative number on failure
*******************************************************************************/
int garageDoorFunction(String sesame)
{
if(sesame.startsWith("C"))
{
if (doorstatus == open)
{
pinMode(0, OUTPUT);
digitalWrite(0, 1);
delay(250);
digitalWrite(0, 0);
return 1;
}
}
else if (sesame.startsWith("O"))
{
if (doorstatus == closed)
{
pinMode(0, OUTPUT);
digitalWrite(0, 1);
delay(250);
digitalWrite(0, 0);
return 2;
}
}
else if (sesame.startsWith("X"))
{
if (doorstatus == closed){
return 999;
} else
return 123;
}
return -1;
}
/*******************************************************************************
* Function Name : updateUbidots
* Description : sends variables to ubidots
* Input : doorstatus global variables
* Output : sends data to ubidots
* Return : void
*******************************************************************************/
void updateUbidots() {
//-------------Begin UBIDOTS---------------
request.hostname = "things.ubidots.com";
request.port = 80;
request.path = "/api/v1.6/variables/"VARIABLE_ID_DOORSTATUS"/values?token="TOKEN;
Serial.println("Sending ubidots data ...");
int ubidotsdoorstatus = ~doorstatus&0x01;
request.body = "{\"value\":" + String(ubidotsdoorstatus) + "}";
// Post request
http.post(request, response, headers);
Serial.println(response.status);
Serial.println(response.body);
request.hostname = "things.ubidots.com";
request.port = 80;
request.path = "/api/v1.6/variables/"VARIABLE_ID_VOLTAGE"/values?token="TOKEN;
Serial.println("Sending ubidots data ...");
request.body = "{\"value\":" + String(analog1) + "}";
// Post request
http.post(request, response, headers);
Serial.println(response.status);
Serial.println(response.body);
}
/*******************************************************************************
* Function Name : getDht
* Description : gets temp and humidity
* Input :
* Output :
* Return : void
*******************************************************************************/
void getDht() {
// Wait at least 2 seconds between measurements.
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a
// very slow sensor)
float h = dht.getHumidity();
// Read temperature as Celsius
float t = dht.getTempCelcius();
// Read temperature as Farenheit
float f = dht.getTempFarenheit();
garagetemp = f; //updates spark variable with current temp.
garagehumid = h;
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Compute heat index
// Must send in temp in Fahrenheit!
float hi = dht.getHeatIndex();
float dp = dht.getDewPoint();
float k = dht.getTempKelvin();
Serial.print("Humid: ");
Serial.print(h);
Serial.print("% - ");
Serial.print("Temp: ");
Serial.print(t);
Serial.print("*C ");
Serial.print(f);
Serial.print("*F ");
Serial.print(k);
Serial.print("*K - ");
Serial.print("DewP: ");
Serial.print(dp);
Serial.print("*C - ");
Serial.print("HeatI: ");
Serial.print(hi);
Serial.println("*C");
Serial.println(Time.timeStr());
}
{
"event": "thingSpeakWrite_",
"url": "https://api.thingspeak.com/update",
"requestType": "POST",
"form": {
"api_key": "{{k}}",
"field1": "{{1}}",
"field2": "{{2}}",
"field3": "{{3}}",
"field4": "{{4}}",
"field5": "{{5}}",
"field6": "{{6}}",
"field7": "{{7}}",
"field8": "{{8}}",
"latitude": "{{a}}",
"longitude": "{{o}}",
"elevation": "{{e}}",
"status": "{{s}}"
},
"mydevices": true,
"noDefaults": true,
"location": true
}
Comments