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();
}
-----

-----

Sunday, December 18, 2016

Low Cost Bi-Directional Level Shift Module Characterization (TE291)

-----
From time to time the level shift module pictured above has come in handy.  They do a great job shifting 3.3VDC to 5VDC logic or 5VDC logic to 3.3VDC logic.  The cost is about $1USD and hookup is simple.  Of course they are designed for low speed digital signals but, we wondered how the module would handle higher speeds.
----
The Bench Setup:
Keithley 2230G-30-1 power supply for the 3.3VDC and 5VDC power.









Tektronix AFG3252C function generator for the square wave (3.3V and 5V logic) stimulus.










Tektronix MDO4104C oscilloscope to capture the input/output signals.

-----
The Result:
Performance was very good, especially considering these module are often used in the 100KHz and below range.  Leveling from 5V to 3.3V had better results.  The signals start looking ridiculous over 1MHz. Take a look at the scope shots below.
-----
3.3V Level Shifted to 5V at 10KHz, 100KHz, 500KHz, and 1MHz
(Yellow = Input; Blue = Output)




-----
 5V Level Shifted to 3.3V at 10KHz, 100KHz, 500KHz, and 1MHz
(Yellow = Input; Blue = Output)




 -----

Thursday, December 1, 2016

Portable ESP8266 WiFi Sniffer (Arduino IDE)

-----
The ESP8266 modules are so cheap who could resist experimenting with one.  For about $8USD a few things you get are 9 GPIOs, I2C/SPI support, an ADC, and on board WiFi or other goodies.  In addition it's all programmable in the Arduino IDE that is familiar to many and has a good user support network.

This example application shows a quick and easy way to get a portable 'WarDriver' with the WiFi ESP8266 and an OLED display.

No resistors, etc. needed; connect it up like this:
https://www.adafruit.com/product/2821https://www.amazon.com/Diymall-Yellow-Serial-Arduino-Display/dp/B00O2LLT30/ref=sr_1_1?ie=UTF8&qid=1480612090&sr=8-1&keywords=diymall+oled

    Pinout Connections
ESP8266                  OLED
3VDC <<==+==>> Vcc
  GND <<==+==>> GND
 SCL(5) <<==+==>> SCL
 SDA(4) <<==+==>> SDA



Take a look at the source code below for the links on installing ESP8266 capability to the Arduino IDE.  Chances are if you are reading this you already have the Arduino IDE installed; just make sure you are running at least Rev 1.6.8.  Then upload the source code to the ESP8266 and your up.
-----
Here is a sample of the rig running in a random parking lot a fair distance from an apartment complex.  Eight networks were found; all encrypted.  The OLED displays the number of networks, SSID name, signal strength (dBm), and if the network is OPEN or Encrypted.
One thing that was a surprise is how many cars have OPEN WiFi running.  Also, pretty much every long haul 18 wheeler heading down the interstate is a rolling WiFi hotspot, but most (not all) are Encrypted.
-----
Here is the source code to push into the ESP8266 via the Arduino IDE:

/*
 * WhiskeyTangoHotel.Com /  NOV2016
 * 'WarDriver' ESP8266 Adafruit HUZZAH w/ WiFi and 32 line OLED
 *   
 *   Scan WiFi networks leverages from:
 *   https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino
 *   
 *   OLED Driver: Thanks, adafruit.com
 *   
 *   Compile for 80MHz with Arduino IDE
 *      Arduino IDE 1.6.8 or greater
 *      http://www.arduino.cc/en/Main/Software
 *      
 *      ESP8266 Board Package
 *      Enter http://arduino.esp8266.com/stable/package_esp8266com_index.json into Additional Board Manage
 *      Restart, select your ESP8266 from the Tools->Board dropdown
 *   
 */
#include "ESP8266WiFi.h"  // API for the ESP8266 WiFi

// Setup the 32 line OLED 
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET LED_BUILTIN  // 4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 32)  // change to 64 for larger OLED. See error trap next line.
#error("Height incorrect, please fix Adafruit_SSD1306.h!"); 
#endif

int screen_roll_delay = 800; // How long to leave Network info on OLED. Delay is executed four times (for LED blink)

void setup() {
  // Setup and test writes to the OLED and Serial Monitor (ESP8266 expects Serial Monitor at 115200 baud)
  
  // Some more OLED setup
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();    // Clear the buffer.
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  
  display.setCursor(0,0);
  display.println("2.4GHz WiFi Scanner");
  Serial.begin(115200); // Display to serial monitor as well as OLED
  Serial.println("Setup begins....");

  // Set WiFi to station mode and disconnect from an AP if it was previously connected
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  Serial.println("Setup completes!!!");
  display.println("------------------");
  display.println("WhiskeyTangoHotel");
  display.println("      .Com");
  display.display();
  delay(5000);  // Welcome/Test screen delay.
}  // end void setup

void loop() {
  Serial.println("Scan starts...");

  // update OLED
  display.clearDisplay();
  display.display();
  display.setCursor(0,0);
  display.println("Scanning...");
  display.display();

  // WiFi.scanNetworks will return the number of networks found as variable "n"
  digitalWrite(0, HIGH);  // On board LED ON
  int n = WiFi.scanNetworks();
  Serial.println(" and completes!!!");
  
  if (n == 0) {  // No WiFi found.  Update the Serial Monitor and the OLED
    Serial.println("No WiFi found!!!");

    // update OLED
    display.clearDisplay();
    display.display();
    display.setCursor(0,0);
    display.println("Scanning...");
    display.println("No WiFi found!!!");
    display.display();
    digitalWrite(0, LOW);  // On board LED OFF
  }      // endif n=0 (no wifi found)
  else  // wifi found
  {
    Serial.print(n);
    Serial.println(" Networks found:");
    Serial.println("----------------");

    for (int i = 0; i < n; ++i)
    {
      digitalWrite(0, HIGH);  // On board LED ON
      
      // Print SSID and RSSI for each network found to Serial Monitor. Show SSID, Signal strenght, and OPEN or Encrypted
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(": ");
      Serial.print(WiFi.RSSI(i));
      Serial.print("dBm | ");
      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?"Not Encrypted":"Encrypted");

      //update OLED with found WiFi. Show SSID, Signal strenght, and OPEN or Encrypted
      display.clearDisplay();
      display.display();
      display.setCursor(0,0);
      display.print("Network ");
      display.print(i+1);
      display.print(" of ");
      display.println(n);
      display.display();
      display.println(WiFi.SSID(i));
      display.println("-------------------");
      display.print(WiFi.RSSI(i));
      display.print("dBm | ");
      display.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?"OPEN":"Encrypted");
      display.display();

      delay(screen_roll_delay);  // Little delay to allow time to read OLED. Flash the on board LED just for fun.
      digitalWrite(0, LOW);   // On board LED OFF
      delay(screen_roll_delay); 
      digitalWrite(0, HIGH);   // On board LED ON
      delay(screen_roll_delay);  
      digitalWrite(0, LOW);   // On board LED OFF
      delay(screen_roll_delay); 
    }  // end for/next loop for n# of wifi networks found
  }   // endif wifi found (n was <> 0)
  Serial.println("");
}  // end void loop (endless)
-----
Thanks for the visit.

Tuesday, October 25, 2016

Prime Numbers in a Box

-----
How many times have you needed the next prime number in a sequence and, like some animal, had to go to a printed table to look it up.  Well, those days are over.
----
A prime number is any positive whole number that can only get evenly divided only by 1 and itself.  Primes are used in many applications; a popular use being for encryption and cryptography.  Demonstrated here is another use for prime numbers.  That is making use of an older/slower Raspberry PI and a few parts to nerd up the decor of any room.

A few Raspberry PI skills learned will be:
     - writing to text files
     - reading from text files
     - driving a low cost I2C LCD display
     - driving a relay via a transistor
     - simple graceful shutdown method for the RasPI with a button and a JST connector.
-----
 
The project has an entertaining audio effect if you are into numbers.  Primes go on forever and ever; infinitely large.  The smallest numerical difference between two primes is 2 (example: 7-5=2).  What is interesting is the distance (difference) between two consecutive primes stays relatively low as the primes become very large.  Press a button and Primes in a Box gives a audible (relay click) signal for each non prime as it waits to display the next found prime.  If you enjoy mathematics you may find this oddly relaxing.
-----
The python source is pretty straight forward.  On button press a pointer to a file containing the first few million primes is indexed and displayed on the LCD.  A 5VDC relay clicks to represent the non primes in between.  The rig runs via USB power and the last found prime is always saved.  A handy 'shutdown' button is incorporated to allow the Raspbery PI project to be powered down gracefully if it needs to be moved.
-----
You'll need a Raspberry PI, 5VDC relay, PN2222A transistor, 16x2 I2C LCD, two resistors, and two normally open button switches.  A project box holds it all together.  Connect it all up like this:
Once on the breadboard it will look a bit like this:
 -----
Set up the RasPI to run the python code below at reboot (use @reboot in the sudo crontab).
Note, two files are expected to be found in the working directory:
     - prime_list.txt (List of prime numbers in order. One per line. As many as you like)
     - high_prime.txt (Holds the highest prime found in case of a restart/reboot)

#!/usr/bin/python

#
#  WhiskeyTangoHotel.Com
#  OCT 2016
#  
#  Program reads a file and displays prime # on LCD
#  On button press find next prime #.  Click a relay for each non prime
#  Write new highest prime to file to save if restarting the program
#
#  Gives an audible (relay click) representation of the distance between primes
#
#  Expects program to be in: /home/pi/RasPI/Programs-RasPI/Prime_Relay/
#
#  Leverages LCD script using I2C backpack for 16x2 and 20x4 screens.
#  Thanks to Matt Hawkins  http://www.raspberrypi-spy.co.uk/
#
#--------------------------------------

import smbus  # for LCD I2C display
import time # fpr sleep and pause delays
import os # this is for the shutdown button press
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD) # to use Raspberry Pi board pin numbers
# set up GPIO output channel
Relay_Pin = 11 # pin to drive the relay clicks via 2222a transistor
GPIO.setup(Relay_Pin, GPIO.OUT)
Relay_delay = .1  # time delay between relay clicks

Switch = 8  # Button switch to move to next prime number
GPIO.setup(Switch, GPIO.IN)

reboot_pin = 26 #  Push this button and the RasPI shuts down gracefully
#Set pin to input and set pull-up resistor to hold the pin is high
GPIO.setup(reboot_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#######################################
### LCD DRIVER FUNCTIONS START HERE ###
#######################################

# Define some device parameters
I2C_ADDR  = 0x27 # I2C device address
LCD_WIDTH = 16   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1 RasPI(Bob)

def lcd_init():
 # Initialise display
 lcd_byte(0x33,LCD_CMD) # 110011 Initialise
 lcd_byte(0x32,LCD_CMD) # 110010 Initialise
 lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
 lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
 lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
 lcd_byte(0x01,LCD_CMD) # 000001 Clear display
 time.sleep(E_DELAY)

def lcd_byte(bits, mode):
 # Send byte to data pins
 # bits = the data
 # mode = 1 for data
 #        0 for command

 bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
 bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT

 # High bits
 bus.write_byte(I2C_ADDR, bits_high)
 lcd_toggle_enable(bits_high)

 # Low bits
 bus.write_byte(I2C_ADDR, bits_low)
 lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
 # Toggle enable
 time.sleep(E_DELAY)
 bus.write_byte(I2C_ADDR, (bits | ENABLE))
 time.sleep(E_PULSE)
 bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
 time.sleep(E_DELAY)

def lcd_string(message,line):
 # Send string to display
 message = message.ljust(LCD_WIDTH," ")
 lcd_byte(line, LCD_CMD)
 for i in range(LCD_WIDTH):
  lcd_byte(ord(message[i]),LCD_CHR)
  
#######################################
### LCD DRIVER FUNCTIONS END HERE  ###
#######################################

# Highest calulated prime is stored in a file.  Get that number
highprime = open('/home/pi/RasPI/Programs-RasPI/Prime_Relay/high_prime.txt','r')
storedprime = (highprime.readline())
storedprime_val = int(storedprime)
print ' '
print "Recalled highest prime from file is: " + storedprime
print ' '

#Put status on LCD
lcd_init()
lcd_string("Init to last",LCD_LINE_1)
lcd_string("prime: " + str(storedprime_val),LCD_LINE_2)
highprime.close

def main():
 # Main program block 
 
 # Relay test clicks
 for i in range(1, 3):  
  GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
  time.sleep(Relay_delay) 
  GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
  time.sleep(Relay_delay)  
 
 # Set the pointer to the correct place in the file that holds the prime list
 # prime_list.txt contains primes to 1,299,709
 primefile = open('/home/pi/RasPI/Programs-RasPI/Prime_Relay/prime_list.txt','r')
 currentprime_val = 0

 while storedprime_val > currentprime_val:
  currentprime = (primefile.readline())
  currentprime_val = int(currentprime)
  
 # Update the LCD
 lcd_string("PRESS for next",LCD_LINE_1)
 lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)
 time.sleep(0.5)
 
 # Relay test clicks
 for i in range(1, 3): 
  GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
  time.sleep(Relay_delay) 
  GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
  time.sleep(Relay_delay) 
 
 while True:
  
  if (GPIO.input(reboot_pin) == 0):
   # Update the LCD
   lcd_string("Primes in a Box",LCD_LINE_1)
   lcd_string("shutting down!!!" + str(currentprime_val),LCD_LINE_2)
   time.sleep(5)
   #Send command to system to shutdown
   os.system("sudo shutdown -h now")
  
  if (GPIO.input(Switch) == 0):
   # Update the LCD
   lcd_string("Calculating next",LCD_LINE_1)
   lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)
  
   oldprime = currentprime_val
   currentprime = (primefile.readline())
   currentprime_val = int(currentprime)
   deltaprime = currentprime_val - oldprime 
   
   for i in range(1, deltaprime+1):  # click relay for each non prime between primes
    #print i
    GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
    time.sleep(Relay_delay) 
    GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
    time.sleep(Relay_delay)   

   #Write the prime to a file so it can be recalled at prog start
   hp = open("/home/pi/RasPI/Programs-RasPI/Prime_Relay/high_prime.txt","w")
   hp.write(str(currentprime_val))
   
   print "CURRENT " +  str(currentprime_val)
   print "    OLD " + str(oldprime)
   print '------- '
   print "  DELTA " +  str(deltaprime)
   #print i
   print ' '

   # Update the LCD
   lcd_string("PRESS for next",LCD_LINE_1)
   lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)

   hp.close
   time.sleep(.250)

if __name__ == '__main__':

 try:
  main()
 except KeyboardInterrupt:
  pass
 finally:
  lcd_byte(0x01, LCD_CMD)
----
Thanks for the visit and happy prime numbering!

Sunday, September 18, 2016

Keep Tabs on the Devices Connected to Your LAN

-----
THE PROBLEM:
Like most people, we have tons of things connected via WiFi to our router.  Be it AppleTVs, Rasberry PIs, Amazon Echos, weather stations, overhead airplane trackers, security cameras, etc. etc. etc....  The problem arises when you think and expect a device to "work" and it doesn't.  Is it the device? Is it the service being accessed? VNC server not running?  Most of the time "all is well", but if it isn't it can be frustrating.  Who knew the security camera was down?  Why can't I VNC into that Raspberry PI?  Most of us have been there and this project helps solve the problem.
------
PROBLEM SOLVED:
We had in the 'warehouse'  a Raspberry PI Zero (mainly because it was cheap so why not) and a BLINKT (who doesn't like flashy light things) doing nothing and just waiting for a project.  Setting up the gear to monitor our LAN seemed perfect for the pair.

The solution is pretty straight forward: Get the RaspBerry PI Zero on your LAN via WiFi.  Plug in the BLINKT module.  Every thirty seconds or so send a ping to an IP of a device on your network.
If the device IP ping is good then
     - flash the BLINKT green.
If the device IP ping is bad then
     - retest the device just to make certain the failure wasn't an anomaly.
     - two failed pings back to back then flash the BLINKT red and log the failure to a Google Drive Sheet.
-----
RESULTS:

The rig works perfectly.  We can check the Google Drive Sheet or the BLINKT quickly see if anything on the LAN is down.  We use IFTTT.com to log the failures to the Google Drive Sheet.  We can just as easily use IFTTT.com to send out a text message, post a Tweet, call our phone, or a host of other IFTTT.com supported actions.  In short, it is pretty darn useful.

Here is what a sample output to the Google Drive Sheet looks like:
-----
SOURCE:
You will need an IFTTT.com and Google logons if you want to log ping status to a Google Drive Sheet.  Both are useful for a few hundred reasons, so if you don't have one then get one - they are free.

#!/usr/bin/env python

# LocalPing Testing/Tracking
# RasPI Zero with BLINKT module attached
# BLINKT scans back/forth with delay in mS = to CPU % use (45% = ,45 Secs)
#
# After a set delay (var = Ping_test_how_often)
# Checks if tracked IPs are up on the LAN. 
#      - If device ping passes, flash all BLINKT LEDs green.
#            - however, if there was a ping fail in the last xx (var = Ping_check) flash 1st LED as RED and rest as GREEN.
#      - If device ping Fails two pings back to back:
#            - use IFTTT Maker Channel to log the failed ping to a Google Drive sheet
#            - write the falied ping information to the terminal
#
# WhiskeyTangoHotel.Com - AUG2016

import os # allows for the ping calls
import time # for sleeps and localtime prints
import random # for random number generation
import psutil # for ping system calls
from blinkt import set_pixel, show, set_brightness, clear # https://shop.pimoroni.com/products/blinkt

# Set up for IFTTT triggering
import requests # needed to get https request to IFTTT web page
MAKER_SECRET_KEY = "xxxyyyzzz1234561890"  #this is your IFTTT Maker Channel secret key

PingSiteIP = [1,2,3,4,5,6,7,8,9,10,11]   # holds the IP or DNS name of the site being pinged
PingSiteName = [1,2,3,4,5,6,7,8,9,10,11]   # Friendly/Human name for the device

# Identify IPs that you want to track on your LAN
# PingSiteName[] can be anything. It is the "human" name for the IP
# Enter the 'x' part of the IP; the last number of the IP address without the '.'
IP_Prefix = "192.168.0."  # typically 192.168.0. or 192.168.1.  DON'T FORGET THE LAST '.'
PingSiteIP[1] = "11"
PingSiteName[1] = "RasPI(Upper)"
PingSiteIP[2] = "14"
PingSiteName[2] = "RasPI(Lower)"
PingSiteIP[3] = "20"
PingSiteName[3] = "Security(NW)"
PingSiteIP[4] = "23"
PingSiteName[4] = "Security(Drive)"
PingSiteIP[5] = "32"
PingSiteName[5] = "Security(SE)"
PingSiteIP[6] = "13"
PingSiteName[6] = "ElectricImp"

PingSiteIP[7] = "27)"
PingSiteName[7] = "RasPI(Airplanes)"
PinsSiteIP[8] = "49"
PingSiteName[8] = "RasPI(Weather)"
PingSiteIP[9] = "45"
PingSiteName[9] = "Printer(HP)"

Num_ping_sites = 9   # Make sure this matches the above number of site entries

Single_test_site = 1 # Must be 1; I did not want to start at array 0
Flash_on = .05       # Secs to keep ping status LEDs on
Flash_off = .2       # Secs to keep ping status LEDs off
Failed_ping = 0      # FLAG for if there is a failed ping in the last Ping_check trys then turn one green light red
Second_try_delay = 20    # seconds to wait to config falied ping on device (Yellow LEDs scan during this delay)
Ping_check = 15         # see comment above for Failed_Ping
Ping_check_counter = Ping_check # Counts Ping_check, decrements and resets at Zero
Ping_test_how_often = 10 # 10 is ~30 secs depending on delay from CPU load. Dont ping test on every cpu speed check
Every_x_times = Ping_test_how_often # decrementing counter for the Ping_test_how_often var
set_brightness(0.05) # 0.05 is lowest usable dim.  1.0 is full bright (the BLINKT is *really* bright if you want!)
LED_Delay = 0        # Controls the LED to LED scan speed. This is adjusted in the program after reading CPU % load
r_largest_wanted_value = 10  # these vars control the max amount of R, G, or B you desire. Also helps with more dimming.
g_largest_wanted_value = 10  # during the CPU load status LED back and forth scan on the BLINKT
b_largest_wanted_value = 255 # For example: 10, 10, 255 would mean favor BLUE hues.

Current_day = time.strftime("%d", time.localtime()) # what day of the month is it
New_day_dawned = Current_day # 1st run after midnight update the Google Sheet. Use to verify the prog has not stopped running
Failed_since_midnight = 0

print " "
print "Writing header information to Google Sheet..."
#Triggers the IFTTT Maker channel "local_ping" to create an information header
url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "*************************"
res = requests.get(url)
#print str(res) + " is web request result. "   #used only for debug

print "Testing LEDs..."
print "---------------------------------------------"
#LED test and some delay for the IFTTT calls
for j in range (0,8):
    set_pixel(j, 30, 0, 0)
show()
time.sleep(Flash_on * j)
for j in range (0,8):
    set_pixel(j, 0, 30, 0)
show()
time.sleep(Flash_on * j)
for j in range (0,8):
    set_pixel(j, 0, 0, 30)
show()
time.sleep(Flash_on * j)   

for sheet_header in range (1, Num_ping_sites + 1):
    #this triggers the IFTTT Maker channel "local_ping" to update the google sheet.
    url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "Tracking " + PingSiteName[sheet_header] + "&value2=" + IP_Prefix + str(PingSiteIP[sheet_header])
    res = requests.get(url)
    #print str(res) + " is web request result. "   #used only for debug
    print str(sheet_header) + ") " + PingSiteName[sheet_header] + " at " + IP_Prefix + str(PingSiteIP[sheet_header])
   
    #LED test and some delay for the IFTTT calls
    for j in range (0,8):
        set_pixel(j, 30, 0, 0)
    show()
    time.sleep(Flash_on * j)
    for j in range (0,8):
        set_pixel(j, 0, 30, 0)
    show()
    time.sleep(Flash_on * j)
    for j in range (0,8):
        set_pixel(j, 0, 0, 30)
    show()
    time.sleep(Flash_on * j)       

clear() # all LEDS off
   
url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "*************************"
res = requests.get(url)
#print str(res) + " is web request result. "   #used only for debug
print " "
print "Done with test!!!"
localtime = time.asctime( time.localtime(time.time()) )
print localtime + ": Tracking begins for the devices listed..."
print " "

time.sleep(1) # let CPU% settle from prog load

while True:
    #LED delay is established randomly
    #random.seed() # seed off the CPU clock
    #r = random.random()
    #LED_Delay = (int((r * 10) + 1)) / 100.0
   
    #LED delay is equal to %CPU usage in mSec (45% = .45 Secs)
    LED_Delay = psutil.cpu_percent() / 100.0

    random.seed() # seed off the CPU clock
    r = random.random()
    red = int((r * r_largest_wanted_value) + 1)
   
    random.seed() # seed off the CPU clock
    r = random.random()
    green = int((r * g_largest_wanted_value) + 1)

    random.seed() # seed off the CPU clock
    r = random.random()
    blue = int((r * b_largest_wanted_value) + 1)   
   
    for i in range(8):       
        set_pixel(i , red, green, blue)
        show()
        time.sleep(LED_Delay)
        clear()

    #LED delay is established randomly
    #random.seed() # seed off the CPU clock
    #r = random.random()
    #LED_Delay = (int((r * 10) + 1)) / 100.0
   
    #LED delay is equal to %CPU usage in mSec (45% = .45mS)
    LED_Delay = psutil.cpu_percent() / 100.0

    random.seed() # seed off the CPU clock
    r = random.random()
    red = int((r * r_largest_wanted_value) + 1)
   
    random.seed() # seed off the CPU clock
    r = random.random()
    green = int((r * g_largest_wanted_value) + 1)

    random.seed() # seed off the CPU clock
    r = random.random()
    blue = int((r * b_largest_wanted_value) + 1)       

    for i in range(7, -1, -1):
        set_pixel(i , red, green, blue)
        show()
        time.sleep(LED_Delay)
        clear()
       
    # Local ping to see if all is up
    # But we don't want to check on every run so:
   
    if Every_x_times == 1: #  var is decremented in the else.  Check the ping
        Every_x_times = Ping_test_how_often
        if Single_test_site == Num_ping_sites:
            Single_test_site = 1
        else:   
            Single_test_site = Single_test_site + 1           
   
        if Ping_check_counter == 0:
            Ping_check_counter = Ping_check
            Failed_ping = 0
        else:
            Ping_check_counter = Ping_check_counter -1       
                   
        hostname = IP_Prefix + str(PingSiteIP[Single_test_site]) # OK, now the have selected one of our ping test sites
        # Ping test IP with os.system command
        response = os.system("ping -c 1 " + hostname + " >/dev/null")
        #print PingSiteName[Single_test_site] + " at IP " + str(PingSiteIP[Single_test_site])
        if response == 0:
            for    i in range (0,10): #flash all green
                for j in range (0,8):
                    if j ==0 and Failed_ping == 1:
                        set_pixel(j, 30, 0, 0)   # red pixel to show a recent failed ping
                    else:
                        set_pixel(j, 0, 30, 0)
                show()
                time.sleep(Flash_on)
                for j in range (0,8):
                    set_pixel(j, 0, 0, 0)
                show()
                time.sleep(Flash_off)               
        else: #response != 0:
            # Ping failed, but wait 5 seconds and try again.
            # Walk yellow LEDs to show re-test and to delay some before double checking the device ping
            for i in range(8):       
                set_pixel(i , 30, 30, 0)  # YELLOW LED walk
                show()
                time.sleep(Second_try_delay/16.0)     
                   
            clear()
            for i in range(7, -1, -1):
                set_pixel(i , 30, 30, 0)
                show()
                time.sleep(Second_try_delay/16.0)
                   
            response = os.system("ping -c 1 " + hostname + " >/dev/null") # let's check again to make certain the ping really failed.
            if response != 0:                                             # it take two back to back failed pings to consider that device down.
                Failed_since_midnight = Failed_since_midnight + 1  # resetable counted tracks the days failed ping count
               
                localtime = time.asctime( time.localtime(time.time()) )
                print localtime + ": Failed ping " + str(Failed_since_midnight) + " from " + PingSiteName[Single_test_site] + " at " + IP_Prefix + str(PingSiteIP[Single_test_site])
               
                #this triggers the IFTTT Maker channel "local_ping" to update the google sheet.
                url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "FailCount[" + str(Failed_since_midnight) + "]:" + PingSiteName[Single_test_site] + "&value2=" + IP_Prefix + str(PingSiteIP[Single_test_site])
                res = requests.get(url)
                #print str(res) + " is web request result. "   #used only for debug
               
                for    i in range (0,10): #flash all red
                    Failed_ping = 1
                    for j in range (0,8):
                        set_pixel(j, 30, 0, 0)
                    show()
                    time.sleep(Flash_on)
                    for j in range (0,8):
                        set_pixel(j, 0, 0, 0)
                        show()
                    time.sleep(Flash_off)
    else: # if Every_x_times == 0:
        Every_x_times = Every_x_times - 1
       
    # Last thing is check if this is a new day.  If yes, this write status to Google sheet
    Current_day = time.strftime("%d", time.localtime())
    if Current_day != New_day_dawned:
        url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + ">>> Failed since midnight: " + str(Failed_since_midnight)
        res = requests.get(url)
        localtime = time.asctime( time.localtime(time.time()) )
        print " "
        print localtime + " >>> Failed since midnight: " + str(Failed_since_midnight)
        print " "
        time.sleep(1)
        Failed_since_midnight = 0
        New_day_dawned = Current_day       

   
-----
Give it a try.  It was been very very useful here.

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)
    -----