"Alexa...turn off the garage door!"

A thrifty open-and-shut challenge

By C.K. Fong, Principal Tech Lead and home automator

C.K. Fong used Alexa to open his garage door

Have you ever laid in bed at night and realized the garage door was wide open? Have you ever stood in front of the garage door button with arms full of groceries and been unable to push it?

Several years ago, I decided I should be able to control my garage door using Alexa. At the time, there were few solutions available, and they were relatively expensive (more than US$100 at the time and still $70 or more). So as a software R&D engineer I decided to see if I could accomplish this for much less.

The heart of this project is the Wemos D1 Mini, a small microcontroller board similar to the much more well-known Arduino Uno. It can be programmed using the same set of software tools! The difference? It's much cheaper and smaller, and even has built-in WiFi. It's great for IoT applications.

List of Parts

Here is a list of parts, as well as the approximate cost if you were to buy them from eBay and have them shipped from China:

- Wemos D1 Mini (~$2.50): WiFi-enabled microcontroller

- Wemos D1 Mini Relay Shield (~$1): Relay used to open/close garage door

- (Optional) KY-024 Digital Hall Effect Sensor (~$1): Sensor for detecting if garage door is open/close

- (Optional) Rare earth magnets (<$1): Placed on garage door as trigger for KY-024 sensor

- Any USB power adapter ($1): Used to power the Wemos D1 Mini; even 500mA is plenty

- MicroUSB cable ($1): For powering the Wemos D1 Mini

- Some hook-up wires: Used for connecting the various pieces together

Total comes up to be US$7.50, excluding hook-up wires. Some items such as USB power adapters and microUSB cables are very common - you probably have spares lying around.

Shipping these items from China for cheap typically takes weeks or months. The same items can be purchased from US sellers for faster shipping times, but be prepared to pay multiple times more.

Programming the Wemos D1 Mini

Much like an Arduino Uno, The Wemos D1 can be programmed by connecting it to a PC using a USB cable.

Additional drivers are needed for the PC to recognize the Wemos D1 Mini. They can be downloaded from this site: https://wiki.wemos.cc/downloads

diagram

To write and download code, the Arduino IDE can be used. This is a free download from the official Arduino site: https://www.arduino.cc/en/Main/Software

The Arduino IDE does not support the Wemos D1 Mini out of the box. This link below shows how to set up the Arduino IDE to work with the ESP8266-based Wemos: https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon

If this new to you, I recommend getting familiarized with the tools at this point and doing something simple such as writing and downloading a code to make the on-board LED on the Wemos D1 Mini blink.

The code uses various libraries such as fauxmoESP and ArduinoOTA. General instructions on how to install additional libraries are available here: https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/common-library-problems?view=all

The fauxmoESP library allows the Wemos D1 Mini to spoof a Philips Hue lightbulb. This allows Alexa to discover and control the Wemos without requiring significantly more work. This library is available here: https://bitbucket.org/xoseperez/fauxmoesp

The ArduinoOTA library provides support for OTA updates. You can update the firmware over WiFi after flashing it using USB the first time! See this link for details, and to install various dependencies such as Python 2.7: http://esp8266.github.io/Arduino/versions/2.0.0/doc/ota_updates/ota_updates.html

Attached is the Arduino code used to program the Wemos D1 Mini.


      /**********************************************************************************
      * Code for controlling the garage door using Amazon's Alexa
      * Written by Chee Keat Fong (chee-keat.fong@hp.com)
      * v2.0 (Apr 22, 2019)
      *********************************************************************************/
    #include 
    #include 
    #include  // v3.1.0
    #include 
    
    #define WIFI_SSID "" // Change your Wifi name
    #define WIFI_PASS "" // Change your Wifi Password
    #define SERIAL_BAUDRATE                 115200
    
    fauxmoESP fauxmo;
    
    // Change pins according to your Wemos Mini D1 pinouts
    const int relayPin = D1; // Digital Relay
    const int sensorPin = D7; // Hall Effect Sensor - don't use D3, D4 or D8, or the board may not boot
    
    const char *deviceName = "Garage Door"; // Name of device for Alexa
    const int doorActivationPeriod = 500;
    bool pulseGarageDoor = false;
    
    // -----------------------------------------------------------------------------
    // Hall Effect Sensor
    // -----------------------------------------------------------------------------
    
    // Reads the sensor to determine if the door is open
    bool GarageDoorIsOpen() 
    {
        // Use this code if the sensor detects the magnet when the garage door is closed
        return (digitalRead(sensorPin) == LOW);  
    
       // Use this code if the sensor detects the magnet when the garage door is open
       // return (digitalRead(sensorPin) == HIGH);  
    }
    
    // -----------------------------------------------------------------------------
    // Wifi Setup
    // -----------------------------------------------------------------------------
    
    void wifiSetup() 
    {
        // Set WIFI module to STA mode
        WiFi.mode(WIFI_STA);
    
        // Connect
        Serial.printf("[WIFI] Connecting to %s ", WIFI_SSID);
        WiFi.begin(WIFI_SSID, WIFI_PASS);
    
        // Wait
        while (WiFi.status() != WL_CONNECTED) {
            Serial.print(".");
            delay(100);
        }
        Serial.println();
    
        // Connected!
        Serial.printf("[WIFI] STATION Mode, SSID: %s, IP address: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
    }
    
    // -----------------------------------------------------------------------------
    // Arduino OTA Setup
    // -----------------------------------------------------------------------------
    
    void arduinoOTASetup() 
    {
      // Port defaults to 8266
      // ArduinoOTA.setPort(8266);
    
      // Hostname defaults to esp8266-[ChipID]
      ArduinoOTA.setHostname("GarageDoor-OTA");
    
      // No authentication by default
      // ArduinoOTA.setPassword((const char *)"123");
    
      ArduinoOTA.begin();
    }
    
    // -----------------------------------------------------------------------------
    // LED blinking helper class
    // -----------------------------------------------------------------------------
    
    class LedBlink
    {
      public:
        void Tick()
        {
          _ticker--;
        
          if (_ticker <= 0)
          {
            if (_tickState)
            {
              digitalWrite(BUILTIN_LED, LOW);
              _ticker = _onTick;
            }   
            else
            {
              digitalWrite(BUILTIN_LED, HIGH);
              _ticker = _offTick;
            }
            _tickState = !_tickState;   
          }       
        }
    
        void Set(int onTick, int offTick)
        {
          _onTick = onTick;
          _offTick = offTick;
        }
    
      private:
        int _onTick = 500;
        int _offTick = 50000;
        int _ticker = _offTick;
        bool _tickState = false;
    };
    
    LedBlink ledBlink;
    
    // -----------------------------------------------------------------------------
    // Device Callback
    // -----------------------------------------------------------------------------
    
    void callback(uint8_t device_id, const char * device_name, bool state) 
    {
      Serial.print("Device "); Serial.print(device_name); 
      Serial.print(" state: ");
      if (state) {
        Serial.println("ON");
      } else {
        Serial.println("OFF");
      }
    
      //Switching action on detection of device name
      if ( (strcmp(device_name, deviceName) == 0) ) 
      {
        // adjust the relay immediately!
        if (state) 
        {
          Serial.println("Try opening garage door");
          // On == Open
          OpenGarageDoor();
        } 
        else 
        {
          Serial.println("Try closing garage door");
          // Off == Close
          CloseGarageDoor();
        }
      }  
    }
    
    // -----------------------------------------------------------------------------
    // Garage Door Control
    // -----------------------------------------------------------------------------
    
    // Don't call this from 'callback', as 'delay' doesn't work from within the callback
    void PulseRelay()
    {
      Serial.println("Pulse start");
      digitalWrite(relayPin, HIGH);
      delay(500);
      digitalWrite(relayPin, LOW);
      Serial.println("Pulse stop");
    }
    
    // Open garage door if it's currently closed
    void OpenGarageDoor()
    {
        if (!GarageDoorIsOpen())
        {
          Serial.println("Really opening garage door");
          pulseGarageDoor = true;
        }
    }
    
    // Close garage door if it's currently open
    void CloseGarageDoor()
    {
        if (GarageDoorIsOpen())
        {
          Serial.println("Really closing garage door");
          pulseGarageDoor = true;
        }
    }
    
    // -----------------------------------------------------------------------------
    // Setup
    // -----------------------------------------------------------------------------
    
    void setup() 
    {
        //Initialize pins to Low on device start
        pinMode(relayPin, OUTPUT);
        pinMode(sensorPin, INPUT);
        digitalWrite(relayPin, LOW);
        pinMode(BUILTIN_LED, OUTPUT);
    
        // LED on
        digitalWrite(BUILTIN_LED, LOW);
        
        // Init serial port and clean garbage
        Serial.begin(SERIAL_BAUDRATE);
        Serial.println("Alexa Garage Door Opener");
        Serial.println("After connection, ask Alexa/Echo to 'turn  on' or 'off'");
    
        // Wifi
        wifiSetup();
    
        // Device names for simulated switches
        fauxmo.addDevice(deviceName);
    
        fauxmo.setPort(80);
        fauxmo.enable(true);
    
        fauxmo.onSetState([](unsigned char device_id, const char * device_name, bool state, unsigned char value) {
            Serial.printf("[MAIN] Device #%d (%s) state: %s value: %d\n", device_id, device_name, state ? "ON" : "OFF", value);
            callback(device_id, device_name, state);
        });
    
        // OTA
        arduinoOTASetup();
    
        // LED on
        digitalWrite(BUILTIN_LED, HIGH);
    }
    
    // -----------------------------------------------------------------------------
    // Loop
    // -----------------------------------------------------------------------------
    
    void loop()
    {
    
      // check for WiFi OTA updates
      ArduinoOTA.handle();
    
      ledBlink.Tick();
    
      // Built-in LED blinks while connected to WiFi
      // If the door is open, the blink lights up longer (useful for diagnostics)
      ledBlink.Set(GarageDoorIsOpen() ? 50000 : 5000, 500000);
    
      // Diagnostics - sensor will control built-in LED directly
      //digitalWrite(BUILTIN_LED, digitalRead(sensorPin));
      
      fauxmo.handle();
    
      // Handle garage door open/close here
      if (pulseGarageDoor)
      {
        PulseRelay();
        pulseGarageDoor = false;
      }
    }

This code requires some modifications before use.

You'll need to replace the strings "" and "" with your WiFi network's actual SSID and password.

The function GarageDoorIsOpen() may also need to be modified depending on how the hall effect sensor is wired up - see below for details.

Powering the system

The Wemos D1 Mini gets power from the microUSB port. As such, it can be powered using any USB phone charger and a regular microUSB cable.

Opening and closing the garage door

First I checked my garage door opener wall switch to determine how it worked. It turned out to be a simple switch that opens or closes the garage door if the two wires connected to it are shorted momentarily. This means that I can use a simple relay controlled by the Wemos D1 Mini to accomplish the same thing. Your garage door switch could be different - please check and adapt accordingly.

Note that the two wires from the garage door switch will run all the way back to the garage door opener that is hanging on the ceiling. This relay could be connected either on the garage door switch or the garage door opener - whichever's more convenient.

Note that this switch is a toggle - it would open the door if it's closed, and close the door if it's open.

Detecting if garage door is open or close

It can be useful to detect if the garage door is currently open or close. Why so? For example, if we can detect that the garage door is already closed and I ask Alexa to close it, the system should just ignore the command and do nothing. Without this detection, asking Alexa to close a garage door that is already closed would actually cause it to open!

The sample code is designed to work with the sensor detecting the magnet when the garage door is closed. If you want to mount this contraption close to the garage door opener on the ceiling, it is probably easier to place the magnet on the top of the garage door in such a way that the sensor detects the magnet with the door open. If so, some simple changes in the 'GarageDoorIsOpen' method are required.

Wiring it all up

Attached is a diagram showing how all the components are wired up.

The magnetic field sensor requires 3 wires - power (+5V), signal and ground. Connect the power and ground to the Wemos D1 Mini's pins, and connect the signal to the D7 pin, which we will configure as an input.

To connect the relay, we use 3 different pins as well - power (+5V), signal and ground. However, this relay is specially designed for the Wemos D1 Mini, and will just stack right on top of the Wemos (assuming the right header pins are used)! We will configure D1 as the output PIN for controlling the relay.

Two wired from the relay (N0 and GND) are then connected to the two wires of the garage door switch.

Setting up Alexa

Once the system is wired and powered up, get Alexa to scan for new devices. The Wemos should show up as 'Garage Door'. This name can be changed in the code by modifying the deviceName variable.

Once detected, remember that Alexa thinks the garage door is a light bulb, so it will respond to "turn on" and "turn off," but not to "open" or "close." To open the door say Alexa: "Alexa, turn on the garage door". It sounds silly, but if it saves you from having to get out of bed at midnight, you can overcome the ridiculousness of mumbling, "Alexa...turn off the garage door."

And that's how you can make your very own voice-controlled garage door opener.


Chee Keat “C.K.” Fong is a Principal Tech Lead on HP’s Personal Systems team at the new HP campus near Houston, Texas. A graduate of Monash University in Australia, he has been cranking out code for more than three decades, programming smartphones, PDAs, and computers. (For our millennial readers, “PDAs” were Personal Digital Assistants – progenitors to the smartphone in your pocket.) C.K. started programming in Applesoft BASIC, then learned C/C++ and Pascal, and now codes in C#. In his spare time he works on automating his home and learning new skills, and likes to play with cool gadgets and have fun with technology.