Monday, October 2, 2017

Garage door opener...or what engineers do.

Heidi got an awesome shirt for me:

So here goes:

I was really tired of my garage door openers working sometimes but not others, and I was totally inspired by my dad redoing his garage door openers with some cool arduino stuff so.

I got:
I already had some wires to connect it all, a hot glue gun, some glue, and some solder and a soldering iron.

The goal was:
  • Spin up a webserver on the ESP8266
  • host a web page with a button to trigger the relay
  • detect if the garage was open or closed and display that on the web page.
Here is the basic wiring diagram

Note that this is an AdaFruit Huzza not my beloved NodeMCU but it's what I had in my fritzing library.

Largely this was not a difficult project except that my soldering skills are not up to snuff yet.

I ran into one gotcha...that is that the 0 pin, D3 on the NodeMCU is triggered when A) the board is being programed or B) power is restored after an outage.  The problem with that is, if I used the 0 pin for the relay, it would get triggered every time the power came back on.
Fortunately the 4 and 5 pins do not have multiple uses and therefore do not trigger on boot, or flash, so I switched the relay to pin 5 and all is well.

I used the phone cord between the NodeMCU and the ultrasonic sensor since I wanted to be able to unplug it at will and I needed a fairly long run. I split the coupler, and soldered one end onto the node, and the other to the sensor, then just ran a $1 phone line between them.

I also  put in a quick plug for the relay to the garage door opener so I could pull it down whenever necessary.

Here is the code:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Ethernet.h>

MDNSResponder mdns;

// Replace with your network credentials
const char* ssid = "MYSSID";
const char* password = "MYNETWORKPASSWORD";

ESP8266WebServer server(80);

String webPage = "";
String webPageOpen ="";
String webPageClosed = "";

int switch_pin = 5; //D1 on nodeMCU
int echo_pin = 0; //D3 on nodeMCU
int ping_pin = 4; //D2 on nodeMCU
int garageStatus = 0; //closed is zero
int minDistance = 10; //inches from sensor
long duration,distance;

void setup(void){
  webPage += "<html><head><title>phatGarage</title>";
  webPage+=".button {";
  webPage+="text-align:center;background-color:#000000;color:#FFFFFF;padding:10px;margin:auto;-moz-border-radius: 10px;-webkit-border-radius: 10px;width:200px;height:100px;";
  webPage+="</head><body><div style=\"max-width:250px\"><a href=\"toggleg\"><div class=\"button\"><h1>Garage Door</h1></div></a><hr/>";
  // if garage is open:
  webPageOpen = webPage;
  webPageOpen += "<h1 style=\"color:red;text-align:center;\">Garage is OPEN</h1><!-- close full page div--></div>";
  webPageOpen += "</body ></html>";
  // if garage is closed:
  webPageClosed += webPage;
  webPageClosed += "<h1 style=\"color:black;text-align:center;\">Garage is Closed</h1><!-- close full page div--></div>";
  webPageClosed += "</body ></html>";

  //Close off clean web page if we need it:
  webPage += "<!-- close full page div--></div></body ></html>";
  // preparing GPIOs
  pinMode(switch_pin, OUTPUT);
  digitalWrite(switch_pin, LOW);
  pinMode(ping_pin, OUTPUT);
  digitalWrite(ping_pin, LOW);
  WiFi.begin(ssid, password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
  Serial.print("Connected to ");
  Serial.print("IP address: ");
  if (mdns.begin("esp8266", WiFi.localIP())) {
    Serial.println("MDNS responder started");
  server.on("/", [](){
    if (garageStatus == 0){
      server.send(200, "text/html", webPageClosed);
    }else {
      server.send(200, "text/html", webPageOpen);
  server.on("/toggleg", [](){
    if (garageStatus == 0){
      server.send(200, "text/html", webPageClosed);
    }else {
      server.send(200, "text/html", webPageOpen);
    digitalWrite(switch_pin, HIGH);
    Serial.println("switch on");
    Serial.println("switch off");
    digitalWrite(switch_pin, LOW);
  Serial.println("HTTP server started");
void loop(void){
  //This does a DNS update if necessary

  //Test distance from sensor:
  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  pinMode(ping_pin, OUTPUT);
  digitalWrite(ping_pin, LOW);
  digitalWrite(ping_pin, HIGH);
  digitalWrite(ping_pin, LOW);
  // The echo pin is used to read the signal from the PING))): a HIGH
  // pulse whose duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  pinMode(echo_pin, INPUT);
  duration = pulseIn(echo_pin, HIGH);
  // convert the time into a distance
  distance = microsecondsToInches(duration);
  //if distance is too close, the garage door is up and being sensed by the sensor, otherwise the sensor is detecting the car or ground which is further away
  //this may need tweaking if the sensor is picking up the suburban as less than 10 inches away.
  if (distance < minDistance){
    garageStatus = 1; //the sensor sees the garage door
    garageStatus = 0; //the sensor does not see the garage door
  //Handle any requests to the webserver

long microsecondsToInches(long microseconds) {
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See:
  return microseconds / 74 / 2;

This was a super fun project, although I'm sure Heidi did not love all the time it took this afternoon...