Showing posts with label relay control. Show all posts
Showing posts with label relay control. Show all posts

Friday, January 6, 2017

ESP8266 WiFi Garage Door Opener from any Web Browser

The 'brain' is the ESP8266 uC.  It is available with on board WiFi and plenty of I/O for smaller projects.  All this for well under $10USD with programming options for NodeMCU, MicroPython, and the Arduino IDE.
-----
There seems to be an unstoppable drive in the hacker DIY community for web based garage door openers and we were compelled to respond.  The garage door opener we have opens/shuts from a push button switch that basically creates a short to connect two terminals on the garage door opening unit.  That allows easy implementation because all that is required is a ESP8266 controlled relay wired across those two terminals to create a switch closure.

In addition to activating the door any activity is logged to a Google Sheet via the IFTTT.com Maker Channel.  This is handy to track all activation usage and ESP8266 server restarts.
-----
The main components are the ESP8266, a relay module, a BS170 N-Channel MOSFET.
 
-----
Simple.  Connect the 'stuff' as shown in the schematic:
and it will look something like this:
----
Use the Arduino IDE to load the source code below into the ESP8266 then wire the Normally Open (NO) side of relay you are controlling to the two terminals on the garage door opener that active the motor when 'shorted' together.

A few comments on the application:
  • Control works from Android, iPhone, PC, etc.  Basically any browser.  In the source code below if a device can open "http://192.168.0.28/long_confusing_URL_to_activate_relay" it will activate the garage door.
  • There is a "TEST" URL in the source code (http://192.168.0.28/) that confirms the ESP8266 is online but does not activate the door.
  • Set a static IP for the ESP8266 in your WiFi router.  Otherwise it may be assigned a different local IP if the ESP8266 or WiFi router is restarted.
  • Use long/complex URLs.  That way those that are connected to your router don't have a 'obvious' URL to activate the rig or one they can remember if you demo it.
  • We only wanted control of the door when connected to the host WiFi router locally (LAN) and not from anyplace on the planet.  If you want extended control to the WWW open a port on your router, but be aware of the concerns. We wanted to limit use only to those authorized to connect the WiFi router locally (LAN).  Plus, we didn't want to risk accidentally activating the door from a remote location.
  • The source code has separate IFTTT.com Maker Channel triggers to log events.  We could use one Maker Channel trigger and just pass different GETPOST variables.  However, creating multiple Maker Channel triggers would easily allow usage tracking on individuals by assigning each one a unique trigger name. (/ZenaActivate, /KelsoActivate, etc...)
  • Any time the door is activated or the ESP8266 is restarted (power outage, etc) a Google Sheet is updated to log the event as shown below. 
 
-----
The Arduino IDE source code is:

/*
 *  Garage Door Opener via Web Browser and log to IFTTT.com
 *  ESP8266 NodeMCU Board using Arduino IDE
 *  WhiskyTangoHotel.Com    DEC2106
 * 
 *  Blue on board LED is active LOW on GPIO2 (D4 on silkscreen)
 *  Relay to control Garage Door is active HIGH on GPIO5 (D1 on silkscreen)
 * 
 *  Opening 192.168.0.28/long_confusing_URL_to_activate_relay is called.  Every effort is made to keep the relay off 
 *  so the door does not close/activate by accident.
 * 
 *  A 'test' message is display on browser to see if server is up by calling root at:.
 *  192.168.0.28/  This WILL NOT ACTIVATE THE RELAY.  Only tests the server
 * 
 *  The 'meat' is at 192.168.0.28/long_confusing_URL_to_activate_relay. This will send a msg to the browser AND open/close the door.
 * 
 */

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

// WiFi Information
const char* ssid = "YOUR-SSID";
const char* password = "ASCII-PASSCODE-FOR-YOUR-SSID";

// IFTTT Information
String MAKER_SECRET_KEY = "xxx-yyy-zzz-123-456-789"; // your maker key here
String TRIGGER_NAME = "IFTTT_Maker_name_to_activate_relay";  // this is the Maker IFTTT trigger name for relay activation
const char* host = "maker.ifttt.com";

ESP8266WebServer server(80);

// Output pins
const int led = 2;  // Blue on board LED
const int relay = 5;  // Relay control line

int randNumber;  // Random# generated just to show a change in the screen.  Help to verify updated page call.

void handleRoot() {
  // This is called if 192.168.0.28/ is requested.  The root.
  // The 'meat' is at /long_confusing_URL_to_activate_relay.
  // This is just here to test the ESP8266 connectivity of the WiFi network without moving the relay
  // Show a message and flash the on board LED.
  randNumber = random(1, 10000);  // Random number just to show a change on the webpage at reload.
  server.send(200, "text/plain", "Testing ESP8266.  Response is: " + String(randNumber));
  digitalWrite(led, 0);  // Active LOW.  Turn On board LED On
  delay(2000);
  digitalWrite(led, 1);  // Active LOW.  Turn On board LED Off
}

void handleNotFound(){
  digitalWrite(led, 1);  // Keep the LED off.
  digitalWrite(relay, 0);  // Keep Relay OFF
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void setup(void){
  pinMode(led, OUTPUT);
  pinMode(relay, OUTPUT);
  digitalWrite(led, 1);  // LED Off
  digitalWrite(relay, 0);  // on power to relay
  Serial.begin(115200);  // serial prints to PC for debug use only
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print("Trying to connect to ");
    Serial.print(ssid);
    Serial.print(" on ");
    Serial.print(WiFi.localIP());
    Serial.println(".");
  }
  Serial.println("");
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
 
  // Write to Google Sheet via IFTTT Maker channel that the ESP8266 has started/restarted

  // Now we trigger the IFTTT Maker Channel to update a Google sheet with the activity of the server starting/restarting
  // This can help log power outs, etc. 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }
 
  // Create the request for IFTTT GARAGE_trigger_serverstart.  This can help log power outs, etc.
  String url = "https://maker.ifttt.com/trigger/GARAGE_trigger_serverstart/with/key/" + MAKER_SECRET_KEY;
  Serial.print("Requesting URL: ");
  Serial.println(url);
 
  // This sends the request to the IFTTT server
  client.print(String("POST ") + url + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
 
  delay(500);  // Delay to for web traffic; maybe not required.
  }

  server.on("/", handleRoot);

  server.on("/long_confusing_URL_to_activate_relay", [](){
    // This is called when 192.168.0.28/long_confusing_URL_to_activate_relay is called
    randNumber = random(1, 10000);  // Random number just to show a change on the webpage at reload.
    server.send(200, "text/plain", "Relay activated @ESP8266.  Code: " + String(randNumber));
    digitalWrite(led, 0);  // Active LOW.  Turn On board LED On
    digitalWrite(relay, 1);  // Relay ON

    // Know we trigger the IFTTT Maker Channel to update a Google sheet with the activity. 
    // Use WiFiClient class to create TCP connections
    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
      Serial.println("connection failed");
      return;
    }
 
    // Create the request for IFTTT
    String url = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME + "/with/key/" + MAKER_SECRET_KEY;
    Serial.print("Requesting URL: ");
    Serial.println(url);
 
    // This sends the request to the IFTTT server
    client.print(String("POST ") + url + " HTTP/1.1\r\n" +
    "Host: " + host + "\r\n" +
    "Connection: close\r\n\r\n");
   
    delay(2000);  // Delay to keep the relay closed.
    digitalWrite(led, 1);  // Active LOW.  Turn On board LED Off
    digitalWrite(relay, 0);  //Relay OFF
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
}
-----

Thursday, July 14, 2016

Home Automation via Bluetooth Proximity Awareness


-----
Bluetooth devices are everywhere and so are Raspberry PI computers.  Here we show how to combine the two for some home automation.
-----
If you are not interested in duplicating the build maybe this summary of what the rig does will change your mind:  Basically this allows any discoverable Bluetooth device (cell phone, smart watch, automobile, tablet, PC, toothbrush, etc.) to control GPIO outputs on the Raspberry PI.  For our use, the Raspberry PI monitors if one of the Bluetooth devices is found in range after being absent for a predetermined time.  If 'yes', turn ON some lighting in house so there is no fumbling around in the dark.  Then, turn the lights back OFF after a predetermined time.  It is easy to modify the Python source for other creative applications.

In addition to controlling the lights, the Bluetooth device "IN/OUT" status and other statistics are logged to the terminal and a Google Drive Sheet via IFTTT.
-----
A while back we did a proof of concept.  In the video above the blue LED turns ON or OFF depending on if the Raspberry PI (with BT-LE dongle) senses a Bluetooth signal from the iPad.  iPad Bluetooth OFF then blue LED OFF.  iPad Bluetooth ON then blue LED on.  From this point it is a trivial task to switch a relay to control a brighter heavier load LED strip in place of the low current blue LED.
 -----
And here is a short video of the completed rig turning ON and OFF a LED strip light.  Note we are toggling GPIO13 (the relay control line) via WebIOPI.  It works the same with the recognized Bluetooth devices, but it would make for a long and boring video to show that.
-----
Materials:
 - Raspberry PI Model B
- WiFi USB dongle (already on board the Raspberry PI3 Model B)
- Bluetooth USB dongle (already on board the Raspberry PI3 Model B)
- Switching relay to control the light Only 1-channel needed, but more allows for expansion.  Spend the extra $1 for a unit like the one in the link with opto isolation built in to help protect your RasPI.
- LED light strip Switch low voltage for safety.  This light is what the relay turns on to light up the room.  Many examples in the link.
- Power supply for the RasPI, wire, maybe a USB hub, a few other very obvious things.
-----
Connect it up like this:

-----
When you get the rig together it will look something like this.  The RasPI pictured has a few extra 'things' attached to it to support our LAN status and "Internet Up" status monitoring projects (in addition to a few other chores).

-----
Now for setting up the software...  The program has comments all throughout the code it to try to make things straightforward.  It is optional, but if you want to log the status of your Bluetooth devices to your Google Drive you need to do a few things first.
- Create a Google account if you don't have one.
- Create an IFTTT account you don't have one.
       - Connect the Google Drive channel and the Maker Channel.
       - Follow the IFTTT directions on creating a Maker Channel trigger to update a Goggle Drive file.
- Establish the Bluetooth devices that will be tracked.  Thanks to prb3333 the utilities and procedures to do this are on their instructables.com site.  This project leverages their work and credits them in the source code below.
- Now, copy the source code below and paste it into your favorite Raspbery PI Python editor.  If you are logging to Goggle Drive with IFTTT adjust the Maker Channel 'secret code' to match the one assigned to your account.  If you decided not to log to Goggle Drive then comment out the IFTTT Maker Channel call statements in the code.
- Enter the Bluetooth addresses you want to track into the variables of the Python source. 
- Adjust the scan variables, number of tracked devices, ON time, etc. variables to meet your application needs.
-------
All the wires connected?  Python script in the RasPI?  Variables adjusted to meet your needs?  Good job!  Save and run the script to enjoy the automation.

The logs to Goggle Drive and the RasPI terminal will look something like this:

 -----
Here is the Python source for the Raspberry PI:

# Bluetooth Device Data Logger and Light Turner Oner
# Logs BT Activity to Google Drive Sheet via IFTTT.Com Maker Channel
#
# WhiskeyTangoHotel.Com
# Based on: http://www.instructables.com/id/Raspberry-Pi-Bluetooth-InOut-Board-or-Whos-Hom/
# JULY 2016

# /home/pi/RasPI/Programs-RasPI/BTLogger/Array_BT_AutoLight.py

#!/usr/bin/python

import bluetooth
import requests # needed to get https request to IFTTT web page
import time
import datetime # to allow timestamp math
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)

Device_name = [1,2,3,4,5,6,7,8,9] # Friendly name of the BT device
Device_ID = [1,2,3,4,5,6,7,8,9]   # BT identifier for device as xx:xx:xx:xx:xx:xx
Last_In = [1,2,3,4,5,6,7,8,9] #Time of last check in.
Last_Out = [1,2,3,4,5,6,7,8,9] #Time of last check out
Device_Status = [1,2,3,4,5,6,7,8,9] # String info on if device is IN or OUT.
Device_Delta = [1,2,3,4,5,6,7,8,9]  # Tracks the time a device has had the same BT check status

# BT device library (Use discovery_BT.py to find devices in discover mode)
# Set up the devices.  Change "Number_of_devices" var below to match.
Device_name[1] = "My iPhone 6S"
Device_ID[1] = "xx:xx:xx:xx:xx:xx"
Device_name[2] = "My Ford Edge Sync"
Device_ID[2] = "xx:xx:xx:xx:xx:xx"

# Program Control variables
Number_of_devices = 2 # this sets the value for the arrays loops in the program
Scan_interval = 30 # time in secs between BT scans.  Too fast will log phantom quick drops; 30 secs is good.
Noise_Filter = 2  # Only log deltas over xx min. Don't be confused by IN/OUT aliasing (worse as # gets larger)
Power_On_Treshold = 30 # Activate relay if device was OUT over xx minutes
Desired_relay_on_time = 10 # Keep the relay activated for xx minutes
# ~~~~~~~~~~~~~~~

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)
# set up GPIO output channel
# I/O 11 = Blinks to confirm prog running
#     13 = Driving the relay
# COLORS TO IO PINS (POS), GND TO NEG
GPIO.setup(11, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)

# Initialize some program variables
changenum = 0 # tracking number of BT status changes.
Relay_On_Time = datetime.datetime.now() # datetime the relay was activated
Number_of_devices = Number_of_devices + 1 # I didn't want to start with array[0]

for jj in range (1, Number_of_devices):
    Device_Status[jj] = " " # Hold the string value "IN" or "OUT" 
    Last_In[jj] = datetime.datetime.now()
    Last_Out[jj] = datetime.datetime.now()
    Device_Delta[jj] = 0 # Time In or Out for each device in minutes.
   
# Setup IFFT keys
MAKER_SECRET_KEY = "1234abcdefg-l234abc12"  #this is your IFTTT secret key
   
print " "
print "Testing LED/Relay/Light..."
for jj in range (0, 5): # blinkie self test LED
    GPIO.output(11,GPIO.HIGH)
    GPIO.output(13, GPIO.HIGH)
    time.sleep(.5)

    GPIO.output(11,GPIO.LOW)
    GPIO.output(13, GPIO.LOW)
    time.sleep(.5)
print "End LED/Relay/Light test."
print " "

print "Preparing for 1st run..."
print "Writing file headers to Google Drive Sheet..."
print " "

# Write header lines for Google Drive file and do some screen prints
url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "*"
res = requests.get(url)
time.sleep(.250)
url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "* Run Conditions are:"
res = requests.get(url)
time.sleep(.250)
url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "* Devices: " + str(Number_of_devices-1) + "&value2=" + "Scan Interval: " + str(Scan_interval) + " secs." + "&value3=" + "Noise Filter: " + str(Noise_Filter) + " mins."
res = requests.get(url)
time.sleep(.250)
url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "* Activate ON thresold: " + str(Power_On_Treshold) + " mins" + "&value2=" + "ON for: " + str(Desired_relay_on_time) + " mins."
res = requests.get(url)
#print str(res) + " is web request result for BT device: "   #used only for debug

Status_bar2 = "       Scan Interval:" + str(Scan_interval) + " secs   |  Noise Filter: " + str(Noise_Filter) + " mins."
Status_bar3 = "Activate ON thresold: " + str(Power_On_Treshold) +  " mins  |        ON for: " + str(Desired_relay_on_time) + " mins."
print Status_bar2
print Status_bar3
print "Tracking the following devices:"
print "--------------------------------------------"

t0 = datetime.datetime.now()  # for timestamp math. t0 is the time the program run started
First_run = 1   # always log intitial status for 1st run
Log_Deactivated = 0
runtime = datetime.datetime.now() - t0   # track how long the program has been running.

while True:  # Main loop forever, and ever...
    # First check to see if the relay should be turned off   
    Should_relay_be_off = datetime.datetime.now() - Relay_On_Time
    Should_relay_be_off = round(Should_relay_be_off.seconds/60.0,1)  # in minutes
    if (Should_relay_be_off >= Desired_relay_on_time):
        GPIO.output(13,GPIO.LOW) # Turn OFF the relay
        if (Log_Deactivated == 1):
            Log_Deactivated = 0
            url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "* Relay DEACTIVATED."
            res = requests.get(url)
            print " "
            print "   Relay DEACTIVATED " + " at " + time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
       
    for check_device in range (1, Number_of_devices):
        # Check to see if BT device is in range
        result = bluetooth.lookup_name(Device_ID[check_device], timeout=5)
        if (result != None):           
            Device_Delta[check_device] = datetime.datetime.now() - Last_Out[check_device]
            Device_Delta[check_device] = round(Device_Delta[check_device].seconds/60.0, 1) # In minutes
            Last_Out[check_device] = datetime.datetime.now() 
            Device_Status[check_device] = "IN.  Hours OUT was: "
            #GPIO.output(11,GPIO.HIGH) #Red RGB on  [debug statement]
           
            # A device is coming back IN. Should the relay be activated?
            if (Device_Delta[check_device] >= Power_On_Treshold):  # then turn on relay
                Relay_On_Time = datetime.datetime.now()
                GPIO.output(13,GPIO.HIGH) # Activate relay
                url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + "* " + Device_name[check_device] + " has ACTIVATED relay."
                res = requests.get(url)
                print " "
                print Device_name[check_device] + " has ACTIVATED relay at " +     time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
                Log_Deactivated = 1 # Flag to log/print when the relay is DEACTIVATED   
        else:
            Device_Delta[check_device] = datetime.datetime.now() - Last_In[check_device]
            Device_Delta[check_device] = round(Device_Delta[check_device].seconds/60.0, 1) # In minutes
            Last_In[check_device] = datetime.datetime.now() 
            Device_Status[check_device] = "OUT   Hours IN was: "
            #GPIO.output(11,GPIO.LOW) #Red RGB off [debug statement]
           
        # Print/Log only BT connection changes that exceed Noise_Filter value
        if (Device_Delta[check_device] >= Noise_Filter):    # comparing values in minutes here       
            url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + Device_name[check_device] + " is " + Device_Status[check_device] + "&value2=" + str(round((Device_Delta[check_device]/60.0),4)) # Write Delta as Hrs
            res = requests.get(url)
            #print str(res) + " is web request result for BT device: "   #used only for debug
       
            changenum = changenum + 1
            runtime = datetime.datetime.now()- t0
            Status_bar1 = "Runtime:" + str(runtime) + " | Status changes:" + str(changenum)
            Status_bar2 = "       Scan Interval:" + str(Scan_interval) + " secs   |  Noise Filter: " + str(Noise_Filter) + " mins."
            Status_bar3 = "Activate ON thresold: " + str(Power_On_Treshold) +  " mins  |        ON for: " + str(Desired_relay_on_time) + " mins."
            # Print BT device status to CRT
            print " "
            print time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
            print Status_bar1
            print Status_bar2
            print Status_bar3
            print "-----------------------------------------------"
            print str(check_device) + ": " + Device_name[check_device] + " is " + Device_Status[check_device] + str(round((Device_Delta[check_device]/60.0),4)) # Write Delta as Hrs

        if (First_run ==1):  # log to Google Sheets/print the intitial status of the tracked devices
            url = url = "https://maker.ifttt.com/trigger/BT_Logger/with/key/" + MAKER_SECRET_KEY + "?value1=" + Device_name[check_device] + " is " + Device_Status[check_device] + "&value2=" + "1ST RUN STATUS."
            res = requests.get(url)
            #print str(res) + " is web request result for BT device: "   #used only for debug
            print Device_name[check_device] + " is " + Device_Status[check_device] + "1ST RUN STATUS."
           
    if (First_run ==1):
        print " "
        print "Tracking begins at: " +     time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        print " "
   
    First_run = 0 # no longer 1st run
   
    # Inverts or 'blinks' the LEDS every .5 secs to confirm program is running
    # Delay until next scan of BT device status.
    for jj in range (0, Scan_interval):    
        GPIO.output(11,GPIO.HIGH) #FLASH Red RGB on
        time.sleep(0.5)     
               
        GPIO.output(11,GPIO.LOW)   #Red LED Off
        time.sleep(0.5)
       
    for jj in range (0, 10):  #Quickie flack of red LED to show new scan starting
        GPIO.output(11,GPIO.HIGH) #FLASH Red RGB on
        time.sleep(0.1)     
               
        GPIO.output(11,GPIO.LOW)   #Red LED Off
        time.sleep(0.1)
    -----