Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
![]() |
| |||||
|
Sharing a garage with neighbors can have its challenges. When our neighbors recently left our garage door open multiple times, raising security concerns, the idea for this project came to me. I wanted to create a system that would notify my family when the door stayed open beyond a specified period, ensuring peace of mind. Using an ultrasonic sensor, a Particle Photon 2, and real-time notifications, this system enhances our garage security and convenience.
Note that this sensor's distance readings can vary due to slight signal interference or environmental changes.
Wiring
Connect the VCC pin of the sensor to the USB pin on the Photon. Attach the GND pin of the sensor to the GND pin on the Photon. Connect the Trig pin of the sensor to any digital pin on the Photon, I used D6. This will be used to send out ultrasonic pulses. Finally, connect the Echo pin of the sensor to another digital pin on the Photon, I used D2.
Particle Photon Code
C/C++This code runs on the Photon microcontroller to monitor the garage door's state using an HC-SR04 ultrasonic sensor. It measures the distance to determine if the garage door is open or closed, then publishes the status as a JSON object to Particle Cloud.
#include "Particle.h"
#include "HC_SR04.h"
SYSTEM_THREAD(ENABLED);
SerialLogHandler logHandler;
// How often to publish a value
const std::chrono::milliseconds publishPeriod = 5s;
// The event name to publish with
const char *eventName = "homeAutomation";
unsigned long lastPublish;
int counter = 0;
void publishTest(const char* doorStatus, int inches);
double cm = 0.0;
double inches = 0.0;
int trigPin = D6; // Trig on D6
int echoPin = D2; // Echo on D2
HC_SR04 rangefinder = HC_SR04(trigPin, echoPin, 1.0, 250.0);
// Threshold for the door being open (greater than 10 cm means door is open)
const double doorOpenThreshold = 10.0;
void setup()
{
Spark.variable("cm", &cm, DOUBLE);
Spark.variable("inches", &inches, DOUBLE);
Serial.begin(9600);
}
void loop()
{
cm = rangefinder.getDistanceCM();
inches = rangefinder.getDistanceInch();
Serial.println(inches);
delay(100);
if (Particle.connected()) {
if (millis() - lastPublish >= publishPeriod.count()) {
lastPublish = millis();
// Check if the distance is greater than the threshold (door open condition)
if (inches > doorOpenThreshold) {
Serial.println("Garage door is open!");
publishTest("open", int(inches));
} else {
Serial.println("Garage door is closed.");
publishTest("closed", int(inches));
}
}
}
}
void publishTest(const char* doorStatus, int inches) {
char buf[128];
// Publish a valid JSON object
snprintf(buf, sizeof(buf), "{\"counter\":%d,\"inches\":%d,\"doorStatus\":\"%s\"}", ++counter, inches, doorStatus);
Particle.publish(eventName, buf, PRIVATE);
Log.info("published: %s", buf);
}
Google Apps Script Code
JavaScriptFollow the setup guide to integrate Particle with Google Sheets: https://docs.particle.io/integrations/community-integrations/publish-to-google-sheets/
Replace your.email@example.com with your actual email address.
Replace your.email@example.com with your actual email address.
function doPost(e) {
Logger.log("Received data: " + JSON.stringify(e)); // Log the entire event object
var publishedAt = new Date(e.parameter.published_at);
var data = {};
try {
data = JSON.parse(e.parameter.data); // Parse the JSON string from Particle
} catch (error) {
Logger.log("Error parsing data: " + error);
return ContentService.createTextOutput(JSON.stringify({ok: false, error: "Invalid data format"}))
.setMimeType(ContentService.MimeType.JSON);
}
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var scriptProperties = PropertiesService.getScriptProperties(); // State tracking
// Retrieve the last known state and timestamp from script properties
var lastStatus = scriptProperties.getProperty("lastDoorStatus") || "closed";
var lastOpenTimestamp = scriptProperties.getProperty("lastOpenTimestamp");
var now = new Date().getTime();
if (data.counter && data.inches && data.doorStatus) {
var row = [e.parameter.coreid, publishedAt, data.counter, data.inches, data.doorStatus];
sheet.appendRow(row);
Logger.log("Row appended: " + JSON.stringify(row));
if (data.doorStatus === "open") {
if (lastStatus === "closed") {
// Door just opened, record the current time
scriptProperties.setProperty("lastOpenTimestamp", now.toString());
} else if (lastStatus === "open" && lastOpenTimestamp) {
// Check if the door has been open for more than 1 minute
if (now - parseInt(lastOpenTimestamp) >= 60000) { // 1 minute in milliseconds
var emailAddress = 'your.email@example.com'; // Replace with your actual email
var subject = 'Garage Door Open Alert!';
var message = 'The garage door has been open for over 1 minute as of ' + publishedAt + '.';
MailApp.sendEmail(emailAddress, subject, message);
Logger.log("Email sent to " + emailAddress);
// Reset the open timestamp to prevent duplicate notifications
scriptProperties.setProperty("lastOpenTimestamp", "");
}
}
} else if (data.doorStatus === "closed") {
// Reset state when the door closes
scriptProperties.setProperty("lastOpenTimestamp", "");
}
// Update the last known state
scriptProperties.setProperty("lastDoorStatus", data.doorStatus);
} else {
Logger.log("Unexpected data format: " + e.parameter.data);
}
var result = { ok: true };
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
}
Comments
Please log in or sign up to comment.