Garage Door Monitor (ESP8266, Google Sheet, 3D Printing)

I’ve been working on this project for quite a while and now I believe it is reproducible.

Thinger.io is the link to:

  • Create a log in a Google sheet
    • V1 used HTTP and a google script to add a row (hard to get started but works fast)
    • V2 uses the IFTTT channl to a add a row (easy to get started but a lot of latency)
  • Use IFTTT to send a notice for reboot or auto-close operation
  • Use API Explorer to turn on and off the Auto-close feature

Next Steps

  • Add external WiFi config
  • Add OTA Update
  • Figure out how to send data to the device from IFTTT, a button on a web page or anything to toggle auto-close feature and to set a different value for the close time.

Additional resources and data at:

Here’s the code for those who are interested:
(Note the next few lines have “//” so that the format in the post is readable. You will need to remove the “//” on the defines and includes.)

#define _DEBUG_
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <ThingerWifi.h>

#define USERNAME "Thinger.io_UserName"
#define DEVICE_ID "Thinger.io_DevID"
#define DEVICE_CREDENTIAL "Thinger.io_Dev_Cred"
#define SSID "WiFiSSID"
#define SSID_PASSWORD "WiFiPassword"
// This script is part of a garage door monitoring system.
// please see https://github.com/Peterh226/GarageDoorMonitor for details

ThingerWifi thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
//External Connections
unsigned int ledOpen = D5;
unsigned int ledClosed = D3;
unsigned int openPin = D7;
unsigned int closedPin = D6;
unsigned int relayPin = D2;
//Global Variables
float closeAfterTime = .083;  //hours
float msCloseAfterTime = 300000;  //milliseconds
unsigned int hourToMillisec = 3600000;
unsigned int oldDoorStatus = 0;
unsigned int doorStatus = 9;
unsigned long doorOpenTime = 0;
unsigned long doorOpenedTime = millis();
unsigned int autoCloseEnable = 1; //set to 0 using API Explorer in Thinger.IO
unsigned int myDoorStatus = 9;

const char* openText = " Door is open";
const char* closedText = " Door is closed";
const char* operatingText = " Door is operating";
const char* autoClosedText = "Door Auto closed";
const char* doorStatusText = "GDM Reboot";

void setup() {
  Serial.begin(115200);
  pinMode(ledOpen, OUTPUT);
  pinMode(ledClosed, OUTPUT);
  pinMode(openPin, INPUT_PULLUP);
  pinMode(closedPin, INPUT_PULLUP);
  pinMode(BUILTIN_LED, OUTPUT);
  pinMode(relayPin, OUTPUT);

  thing.add_wifi(SSID, SSID_PASSWORD);

  thing["doorStatus"] >> [](pson & out) {
    out["State"] = myDoorStatus;
    out["DoorText"] = doorStatusText;
    out["doorOpenTime"] = doorOpenTime;
  };

  thing["AutoCloseToggle"] << [](pson & in) {
    autoCloseEnable = in;
    Serial.print("Auto Close Value: ");
    Serial.println(autoCloseEnable);
  };
}

void loop() {
  Serial.println("******* Loop Start *********");
  thing.handle();
  //update autoclose time if new value is provided
  msCloseAfterTime = closeAfterTime * hourToMillisec;
  Serial.print("closeAfter - msClose: ");
  Serial.print(closeAfterTime);
  Serial.print(" - ");
  Serial.println(msCloseAfterTime);

  if (doorStatus == 9) {
    Serial.println("Notify of reboot");
    callEndpoints();
  }

  //blink heartbeat light to reduce cycles and allow wifi to process
  heartbeatLED();

  //Check status of sensors
  myDoorStatus = doorCheck();

  //Call The endpoints to register if door opened or closed
  //Figure out logic of when and what to send to endpoint
  // See State Table.
  //***if oldDoorStatus = (2 or 3) and myDoorStatus = 1 then call endpoint
  //***if old and my doorstatus = 1, then check time and activate if needed
  //Check if door open longer than closeAfterTime
  //***if old = 1 or 2 and myDoorStatus = 3 then call endpoint
  Serial.print("OldStatus - Status: ");
  Serial.print(oldDoorStatus);
  Serial.print(" - ");
  Serial.println(myDoorStatus);
  if ((oldDoorStatus == 2 || oldDoorStatus == 3) && myDoorStatus == 1) {//Door has Opened
    doorOpenedTime = 0; //since door just opened, put a zero in the spreadsheet
    callEndpoints();
    doorOpenedTime = millis(); //Now the door is open, remember the time
  }
  else if (oldDoorStatus == 1 && myDoorStatus == 1) {//Door is open
    doorOpenTime = millis() - doorOpenedTime;
    if ((doorOpenTime > msCloseAfterTime) && (autoCloseEnable == 1)) {
      Serial.println("Auto Energizing Relay");
      Serial.print("Auto Close Value: ");
      Serial.println(autoCloseEnable);
      energizeRelay();
    }
  }
  else if ((oldDoorStatus == 1 || oldDoorStatus == 2) && myDoorStatus == 3) {//Door has closed
    callEndpoints();
    doorOpenTime = 0;
  }
  else
    //when door is closed, stuck, or broken

    Serial.println("No Action Door Status");

  oldDoorStatus = myDoorStatus;
}

//******************************************************************************************
// Functions

unsigned doorCheck() {
  //Check the status of the switches
  // Open=1, Closed=3, Operating=2, Problem=9
  Serial.print(digitalRead(openPin));
  Serial.print(digitalRead(closedPin));
  if ((digitalRead(openPin) == 1) && (digitalRead(closedPin) == 1)) {
    //Door is operating
    doorStatus = 2;
    digitalWrite(ledOpen, HIGH);
    digitalWrite(ledClosed, HIGH);
    Serial.println(operatingText);
    doorStatusText = operatingText;
  }
  else if (digitalRead(openPin) == 0) {
    //Door is open
    doorStatus = 1;
    digitalWrite(ledOpen, HIGH);
    digitalWrite(ledClosed, LOW);
    Serial.println(openText);
    doorStatusText = openText;
    Serial.print("doorOpenTime: ");
    Serial.println(doorOpenTime);
  }
  else if (digitalRead(closedPin) == 0) {
    //Door is closed
    doorStatus = 3;
    //Set the open time = current time
    doorOpenTime = millis();
    digitalWrite(ledOpen, LOW);
    digitalWrite(ledClosed, HIGH);
    Serial.println(closedText);
    doorStatusText = closedText;
  }

  else {
    //Both switches failed
    doorStatus = 8;
    Serial.println("Door is busted");
    digitalWrite(ledOpen, LOW);
    digitalWrite(ledClosed, LOW);
  }
  return doorStatus;
}

void heartbeatLED() {
  //Heartbeat 5second depay
  //digitalWrite(ledOpen, HIGH);   // turn the LED on (HIGH is the voltage level)
  digitalWrite(BUILTIN_LED, HIGH);
  delay(4000);              // wait for a 4 seconds
  //digitalWrite(ledOpen, LOW);    // turn the LED off by making the voltage LOW
  digitalWrite(BUILTIN_LED, LOW);
  delay(1000);              // LED On for 1 second out of 5
  return;
}

void callEndpoints() {
  Serial.println("Calling Endpoint now");

  //pson data;
  Serial.print(doorStatus);
  Serial.print("---");
  Serial.println(doorOpenTime);
  //add a row to the Google Sheet using IFTTT
  pson txtMsg;
  txtMsg["value1"] = doorStatusText;
  txtMsg["value2"] = doorOpenTime;
  thing.call_endpoint("IFTTT_GDM_Data", txtMsg);

  if (doorStatus == 9) {
    //Notify that system rebooted or auto-closed
    thing.call_endpoint("MakerTest1", txtMsg);
  }
}

void energizeRelay() {
  //close relay to activate door opener
  Serial.println("Relay On");
  digitalWrite(relayPin, HIGH);
  //delay for 1 second to keep relay closed
  delay(1000);
  //notify of autoclose
  unsigned int oldDoorTemp = doorStatus;
  doorStatus = 9;
  Serial.println("Calling endpoint with auto status");
  doorStatusText = autoClosedText;
  callEndpoints();
  doorStatus = oldDoorTemp;
  digitalWrite(relayPin, LOW);
  Serial.println("Relay Off");
  //delay to let door close usually around 13 seconds
  delay(15000);
}
4 Likes

Thanks for your post and your detailed description @Peterh226!! Your project seems to be soo cool and almost complete! Like the 3D printing parts you had done to fit your project in the door! Hope it helps to inspire other people to get started in the IoT.

I think I can help you in two different points of your following next steps. Just tell me when you are ready to follow :slight_smile:

P.S: I have edited your post adding ``` and ``` to the beginning and end of your code, so it looks better!

1 Like

Hi Peter,

I like your project. I’ve been working on a similar project for a while (EchoGarage).

Instead of magnetic reed switches I use an ultrasonic sensor (more portable solution) to detect garage door state but I am struggling to find a way to check that my door is not stuck between open and closed state. Any idea?

Have you made any progress with OTA update? I’d like to implement this feature into my project also.

Best Wishes
Sotirios

Hi alvaro,

I have tried to add ‘’’ and ‘’’ to the beginning and end of my code but it doesn’t seem to work. What am I doing wrong?

Thanks
Sotirios

I updated to use the Maker channel rather than the Google Script. I could not get the script to work right except in my initial implementation.

I have several states that I can track. If you look at my blog on this at http://www.heath-solutions.com/ you’ll see the state diagram. So I know if it is stuck since both switches are open.

I have a timer that closes the door automatically after 5 minutes. I’m trying to use a dashboard to adjust the self close time and also to disable the autoclose (like in the summer if you are outside working). I’m not doing something correct with the widget and the communication with the unit, but it has not bubbled up to the top of my priority list. The autoclose and letting me know when it opens/closes (notification on phone) has been great and I really like it. It’s also super reliable.

I have just implemented AutoClose feature in my project. See below my approach in case you interested in trying to your dashboard.

I use SimpleTimer library to launch timed actions and I am really happy with the way it works.

#include <SimpleTimer.h>                 // Simple library based on millis() to launch timed actions. http://playground.arduino.cc/Code/SimpleTimer

SimpleTimer timer;                       // There must be one global SimpleTimer object.

String currentDoorState = "";            // Holds current door state (open or closed).
unsigned long timeInterval_ms = 0;       // Holds time interval in ms since door was opened. 
unsigned long autoCloseInterval = 0;     // Configuration option in min that used to enable auto close feature, zero means disabled (default 0).
int relayTimerId;                        // Holds the specified Timer slot for energize_relay() function.

/********************************    Functions   **********************************/

void energize_relay() {
  digitalWrite(RELAY_1, LOW); delay(300); digitalWrite(RELAY_1, HIGH);
}

void auto_close(){
  if (currentDoorState == "Closed"){
     timer.disable(relayTimerId);     
     } else if (currentDoorState == "Open" && timeInterval_ms == 0){
     timer.enable(relayTimerId);
     timer.restartTimer(relayTimerId);
     }
}

/***********************************************************************************/

void setup() {

  pinMode(RELAY_1, OUTPUT);              // set pin D0 (GPIO16) output.
  digitalWrite(RELAY_1, HIGH);           // set pin D0 (GPIO16) high.

// input resource to remotelly modify the autoCloseInterval variable defined as a global variable.
  thing["autoCloseInterval"] << [](pson& in){
    if (in.is_empty()){
        in = autoCloseInterval;
        } else {
          autoCloseInterval = in;
          } 
    if (autoCloseInterval == 0){
        timer.deleteTimer(relayTimerId);
        } else {
          timer.deleteTimer(relayTimerId);
          relayTimerId = timer.setInterval(autoCloseInterval * 60000, energize_relay);
          }
    };
}

void loop() {

  timer.run();                         

  thing.handle();

  auto_close();
}

I have added a Slider widget in my Dashboard and I’ve opted for “autoCloseInterval” in Select Resource field. This way every time I drag and drop the slider into zero I completely disable AutoClose. On the other hand, if a choose any value greater than zero I automatically enable AutoClose and adjust the AutoClose time interval accordingly. Really nice! :slight_smile:

I think that with a slight modification it will work also in your project.

Hope this makes sense
Sotirios

This is great and I’m working on adding it to my project. I’m having a bunch of problems connecting with Thinger right now and trying do determine what I’m doing wrong. I’ll update all once I get this all going again!