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

Sunday, April 10, 2016

Graphing Current Drain of the Sparkfun BadgerHack

-----
Sparkfun had a nice booth at SXSW in Austin that let you build and walk away with their BadgerHack project.  Very generous and Thanks!  Also at the booth were some of the 'moviestars' often seen on their YouTube channel.  It was great to meet a few of them.

I was curious about the current drain of all those LEDs dancing on the display.  I had access to a Keithley 2461 SourceMeter/SMU which makes it easy to run the experiment.  The video below tells it all. 

-----
Thanks again, Sparkfun!

Saturday, January 30, 2016

Low Cost Servo Motor Tester from eBay

For about a $1.50 USD eBay sellers will ship out to you the servo tester pictured above.   It's an amazing value to let you verify that the cheap servo you also got from eBay even works.  It's also great to help understand the MIN, CENTER, and MAX positions for any servo based RC or uC project. 
-----
Basically, servos motors are controlled by receiving pulse train signals.  The eBay servo tester is capable of producing this required pulse train.  Jameco has an easy to follow tutorial.  The image below shows that a 1mSec pulse width sends the servo full left.  1.5mSec centers the it.  2mSecs move the servo full right.  Jameco explains it all very well.
-----
The servo tester has three setting:
     MANUAL: allows the servo position to be adjusted with a knob.
     CENTER:   sets the servo to it's center position
     SWEEP:     continually sweeps the servo from it's MAX to MIN position (forever)

Connection is simple.  One side of the servo tester is for power.  The other side is where the servo under test conveniently plugs in.
-----
So.... let's use a Tektronix MSO5104B oscilloscope to look at the output of the servo tester and see what the heck is going on.  In the screen shot below the servo tester control knob is set to full left (minimum).  Sure enough, the pulse width as measured by the scope is 915uSec.  That's pretty darn close to the 1mSec width in the drawing above.
-----
With the knob set to full right (maximum) the scope cursors show a pulse width of 2.14mSec; pretty close to the ideal 2mSec in the drawing.
-----
Using the MSO5104B oscilloscope to zoom out on the signal we can see that the pulses repeat steadily every 20mSec.  If you check the tutorial at Jameco you will know that is exactly what is expected.
  -----
Conclusion:  Splurge the $1.50 USD and treat yourself to this handy device.  It really helps to verify that a servo is actually working and in setting the movement limits on your project.  Below are two short videos of the servo tester in action.
 

 
----
Here a short video where I use a Tektronix AFG3252C to send the control pulses to the servo.  The AFG3252C has infinitely more flexibility, but cost a few dollars more than the eBay option.
 
----- 
Thanks for stopping by.  Build something.


Wednesday, January 13, 2016

5VDC Boost Converter Characterization

 -----
Boost Converters are a type of switch power supply that magically turns a small DC voltage into larger DC voltage.  Learn about how that magic is preformed here.   Used properly they are great, but the magic of a boost converter does have it's limits.
----- 
For the measurement setup a Keithley 2230 power supply is set to 1.5VDC (think AA battery voltage) to act as V(in) for the boost converter.  A Keithley DMM7510 HiRes multimeter is used to measure the ripple on V(out).  A Keithley 2450 SourceMeter SMU is used to simulate a load on the boost converter in static and swept mode. The Keithley 2450 also produces a V(out) graph under the swept load.

-----
The video shows how the 5VDC boost converter starts performing poorly at loads close to 100mA.  The output voltage begins to drop and ripple frequency increases as the boost converter works harder under the higher loads.  The Keithley 2450 is set up to graph the boost converter voltage output as it simulates the swept load.
-----
[EDIT: February 20, 2016]: We got a comment that the boost performance could have been effected by not having a constant 1.5VDC input into the converter during the entire load sweep due to lead resistance.  Turns out the comment was correct.  We retested the same boost converter with 4-wire measurement connections at both the INPUT and OUTPUT.  This 4-wire setup reduces any errors due to lead resistance.  The video below shows the improved result.  Measurement setup is everything!!!
 -----

Wednesday, November 25, 2015

Simple FET Driven Strobe Light

 
We needed a strobe light around the house for some mechanical troubleshooting.  Most 'normal' people would just purchase one, but what's the fun in that.
-----
Our solution was to use a 555 timer chip to drive a MOSFET to drive a 12VDC LED spotlight.  The internet is filed with 555 timer chip examples so we will skip that discussion.  The problem is that a 555 is fine for blinking a standard small LED, but a small LED doesn't put out enough light to be used as a strobe.
-----
The simple schematic below shows how a MOSFET can be used as a simple switch that turns power ON/OFF to the 12VDC strobe LED to match the smaller signal generated by the 555 timer chip.  The nice thing about using a MOSFET as a switch is, being a voltage controlled device, it can work without biasing components (YMMV).
-----
Wire the MOSFET up with some JST connectors for convenience and you get:
-----
I know this isn't exactly "high tech", but the rig works fine.  Below is a short video of it "stopping" a ceiling fan from a distance of about 10 feet:
 -----

Thursday, November 5, 2015

NumerousApp: Easiest way to push RasPI data to your iPhone

-----
As a long time fan and user of IFTTT.com we would see the Numerous Channel from time to time and wonder what it did, how it could be used, etc.  The Numerous tagline is "Numerous follows the most important numbers in your life and keeps them all up to date, all in one place" and that certainly got our attention.
-----
Numerous runs on the iPhone and the app has a fantastic UI.  The developer must be some rare artist/SWE hybrid type or something.  The app flows well, takes advantage of new iOS features (TouchID, Apple Watch, etc.) and is easy to personalize.  The graphs are clear and it even lets you customize when you get notifications (value to big/small, value change, change percentage exceeded, comments updated) and more.  It short; it is a joy to use.

Also, there are dozens of pre-programmed "metrics" that you can follow.  Check them out.  They are interesting and useful, but could get boring and after a while you will want to roll your own metrics to track more personal data useful for your applications.
-----
That's where this blog post steps in.  Numerous is really easy to get your personal data into it.  After only a short time we had created "numerousNumerous App metrics to:
 - track temperature/humidity sensors scattered around the house
 - track Internet UP/DOWN status
 - track security system triggers
 - track UP/DOWN status of LAN devices (security cams, Raspbery PIs, Imps, etc.) 
 - several other miscellaneous things.... 
------
How to get data into Numerous?
One way is to use the Maker Channel on IFTTT.com as a trigger to the Numerous Channel on IFTTT.com.  This requires no coding and is an easy way to pass three variables in one Maker Channel event trigger.  If you want to include the Maker Channel trigger URL into a Python or PHP script the applications are endless.

If you don't mind a little extra Python code, the task is almost as easy and offers more power and flexibility.  To give credit where credit is due, it is made simple with the Python API App by outofmbufs

Here's my simple example using Python and a Raspberry PI to get data to the iPhone.  The example shows how to create a Numerous metric, write data and comments to it, and view a graph of the data.
-----
Run the Numerous app on the iPhone and create a new 'metric'.  See the "+" sign in the upper right.  Click that.
-----
Give your metric a name.  Don't worry about what you call it, you can change it later.  Remember, I told you the app was flexible....
 -----
After you create the metric you can customize the way it looks.  Change the title and description to suit your needs, add a meaningful background image, input data manually, set privacy and notification setting, etc.  There is even a way to set the units.  I put the units as "megaFonzies" in the example to denote how cool the Numerous App is.
-----
Making the appearance pretty is fine, but what we want is to feed the metric data from the Raspberry PI via a Python script.  For that you need two numbers:
                    - your personal and secret API key:  Get this by selecting "Settings/Developer Info" from the main screen of the Numerous app.  Don't share this API key.  It will start with "nmrs_" and is shown as "nmrs_xxxxxxxxxxx" in the source code example below.
                    - the METRIC ID that you are writing to:  In the Numerous app click on the tile for the metric you just created and customized.  In the upper right you will see the classic "menu bar" icon.  Click that then "Developer Info" to find your METRIC ID.

 -----
Modify the Python script below with your API Key and METRIC ID values and run it.  Your metric will start recording the values that you feed it:
-----
Want a graph of the data?  Turn the iPhone to landscape mode.  You can pinch to zoom in and out for both the X and Y axis!  How cool is that?  At least 52 megaFonzies!

-----
That's pretty much it.  The Python script example below will introduce you to all the basics.  It writes the system clock seconds value to the metric, but that could be any value your RasPI can produce or scrape from the web.  The Numerous metric in the example is 'Public' that anyone can view, update, and comment on.  Feel free to use it for testing.  Good luck!
-----
# WhiskeyTangoHotel.com [OCT2015]

# Super easy way to track/graph RasPI data to your mobile device using NumerousApp.
# This simple prog logs the "seconds" value of the localtime value to a Numerous metric.
# The Numerpus mobile app will log, track, and graph the data for you.
# Of course, this value could be a sensor reading, CPU usage, Core Temp, or any other numerical value.

# D/L the NumerousApp and install it on your mobile device.
#            iPhone: https://itunes.apple.com/us/app/numerous-lifes-most-important/id797642904?mt=8
#   Google Play: https://play.google.com/store/apps/details?id=com.numerousapp

#
# See http://numerousapp.com/ for full APIs and other really cool applications.

# https://github.com/outofmbufs/Nappy/blob/master/README.md

# Also check out the Numerous integration to IFTTT.com for more awesomeness.
# Numerous can act as a trigger and receive triggers from most IFTTT.com channels
# Teaming the Maker channel on IFTTT.com with Numerous is VERY powerful!

# 1st, do a one time install from the terminal using "sudo pip install numerous"
# Must have pip installed to do this: "sudo apt-get install python-pip"
# Needed for the following import line.
from numerous import Numerous   

import time                                              # Needed to get the system clock settings

MyKey = "nmrs_xxxxxxxxxxx"             # Your personal Numerous App API secret key.  

MyMetric = "2884060434653363716"    # Get this by clicking "Developer Info" from the numerous app
                                                                  # "2884060434653363716" is set as an open/public metric 

                                                                  # Anyone can write to, so you just enter your personal API key to test.
                                                                  # Link to load the metric to the app: https://nmrs.co/m/lwtm3kymv2tg
                                  
# Set up for calls
nr = Numerous(apiKey=MyKey)
metric = nr.metric(MyMetric)

# This will return the Numerous App Label string.
label = metric['label']

# This will return the current value of the metric
Current_value = str((metric.read()))
 

# Let's get the current seconds value of the system clock, convert that
# to a number, and write that seconds value to the Numerous metric
Timestamp_seconds = int(time.strftime("%S", time.localtime()))
metric.write(Timestamp_seconds)

# Uncomment the next line to add a like (a thumbs up) to the Numerous metric
#metric.like()

# This will add a comment to the Numerous metric
Comment_update = "System timestamp is: " + time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
metric.comment(Comment_update)

#Print info to the screen
print " "
print time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "Metric Name: " + label
print "----------------------"
print "Current value:" +  Current_value                # returns last current value of the Numerous metric   
print "New value: " + str(Timestamp_seconds)    # the value written to NumerousApp

-----

Tuesday, September 1, 2015

Raspberry PI answers, "Is the internet up?"

 
It is a pretty commonly uttered question (sometimes loudly) around the home or office; "Is the internet up?"  As the goto IT Support Manager around the house this can get a little tiresome.  Most of the time the internet is up and it's a user or personal device problem that can be solved with a re-boot, re-load, re-etc.  Sometimes the internet is really down requiring a router and cable modem reboot or a call to the ISP.  Wouldn't a simple visual check that anyone could quickly understand be helpful?  Plus, as a bonus, provide some general network health statistics with a screen print and an hourly update to a Google Drive spreadsheet.

This project is amazingly useful and incredibly simple to duplicate.  If you already have a Raspberry PI it is very cheap to implement.
-----
What it does and how it works:
The Raspberry PI runs a Python program that test pings 10 internet sites that you to determine to provide the status of your internet connection.

      GREEN: 7 or more sites return a successful ping.
                      Green LED on.  Updates the stats.
  YELLOW: 6 to 4 sites return a successful ping.
                     Yellow LED on.  Updates the stats.
           RED: 3 or fewer sites return a successful ping.
                     Red LED on.  Updates the stats.

To make the program run faster and not burden the network with outgoing pings, all 10 sites are not pinged on each run.  The ping test rotates through the 10 sites.  If the selected site does fail the ping test, the program goes into "Deep Probe" and checks all 10 sites to determine the connection status.  "Deep Probe" runs are tracked for the status report that prints to the screen.

Each hour a status report is appended to a Google Drive spreadsheet via the Maker channel at IFTTT.Com.
-----
For a visual output we went with a device that anyone can identify with, a simple $8 toy traffic light from Amazon.  For $8 you get a lot.  The light is well build, looks great, and is a full 7" tall with three LEDs already installed.  It also comes with 12 little toy cars that you can give away; you don't need those.  Next remove the four screws at the bottom of the base.  You are going to see four wires that lead up to the three LEDs in the traffic light.  Cut those wires and free them from the little ON/OFF switch and microcontroller that was meant to control the blink patterns for the non-hacker.  Now you should only be left with four wires.  The LEDs are wired common athode (+).  The simple schematic below shows the hookup for each LED and their connection to the Raspberry PI.  Double check the wiring for your traffic light with a button cell battery to see what wire lights each LED.
 
-----
You don't need to open the back, but if you did it would look like this:

-----
After you wire in the toy traffic light it will look something like this:
----- 
Now you will need to load the Python source code provided below, but before you do, a few simple edits are required:
   - Find in the code where it says "# Identify ten PING 'friendly' sites".  In that section put in ten IP addresses or DNS names that are "ping friendly".  Not all sites allow you to ping them, so test ping them manually first from a command line.
   - Find the variable named "Delay_between_tests".  In the program it is set to 15 (for 15 seconds).  Adjust this if you want to test more or less frequently.
-----
That's about it, now run the program.  The RasPI will take the traffic light through an LED self test.  Then it will do an initial first run deep probe ping test on all ten of the test sites; the LEDs will self test during this as well.
-----
From then on the little toy traffic light will answer that all too common "Is the internet up?" question.  In addition, as the local IT Support Manager, you will have access to a running history in a Google Drive spreadsheet and the status output table below to impress your friends:

-----
Here is the Python code that makes it all work.  Of course, as mentioned above, you will need to supply your own list of ten ping friendly internet sites instead of "yourtestsite-1.com", etc.

# Is the Internet UP?  /    August 2015
#
# Project details at:
# http://whiskeytangohotel.com/internetup
#
# Using a Raspberry PI we ping a 10 IP addresses and light up a Red, Yellow, or Green LED
# to display the 'health' of our internet connection. 
#
# SEPT 3, 2015: Adding IFTTT.com Maker channel to log status to Google Drive each hour.
#
# At least 7 of 10 successful pings to light the Green LED
# 3 or fewer of 10 successful pings for Red LED
# Anything in between and light the Yellow LED
#
# Program does not ping all 10 sites each time.  For speed, etc. one of the sites
# is tested  at each run.  If that ping fails, 'deep probe' mode involkes and all sites are tested
# to determine latest Red/Yellow/Green status.
#
# As an output device we used a $7 toy stoplight from Amazon.com. Search:
# "Dazzling Toys Racer Cars and Traffic Set - 7" Traffic Light with 12 Pull Back 2" Racer Cars"
#
# In addition to the LED stop light status. The details below are screen printed on each run.
#
# Tue, 01 Sep 2015 05:03:32.  Update interval: 15 seconds.
# Status report: GREEN.  Runs: 1812.  DeepProbes: 6.  Runtime: 8:42:44.448047
# -----------------------------------------------------------------------------------
#        Last check:  UP      pingfriendlysite-1.com was UP 188 of 189 pings.  99% uptime.
# >>>Last check:  UP      pingfriendlysite-2.com was UP 189 of 189 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-3.com was UP 187 of 188 pings.  99% uptime.
#        Last check:  UP      pingfriendlysite-4.com was UP 187 of 188 pings.  99% uptime.
#        Last check:  UP      pingfriendlysite-5.com was UP 188 of 188 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-6.com was UP 188 of 188 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-7.com was UP 188 of 188 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-8.com was UP 188 of 188 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-9.com was UP 188 of 188 pings.  100% uptime.
#        Last check:  UP      pingfriendlysite-10.com was UP 185 of 188 pings.  98% uptime.
# ------------------------------------------------------------------------------------
#   
#     RED:     0 events.  Last event: none detected
#  YELLOW:     0 events.  Last event: none detected
#   GREEN:  1812 events.  Last event: Tue, 01 Sep 2015 05:03:32
#

from __future__ import division # don't round  interger division to  'zero'
import time 
import datetime # used for timestamp math
t0 = datetime.datetime.now()  # for timestamp math. t0 is the time the program run started
runtime = 0 # localtime minus t0 = total runtime of the program

# Set up for IFTTT triggering
import requests # needed to get https request to IFTTT web page
Current_hr = time.strftime("%H", time.localtime()) # use this var to update trigger IFTTT at each hour change
IFTTT_hr = "999" # we will only trigger IFTTT if this is diffent from Current_hr
MAKER_SECRET_KEY = "xxxx-my-maker-ifft-key-xxx"  #this is my IFTTT Maker Channel secret key

Delay_between_tests = 15 #in seconds. Delay is in last line of code
Loop_count = 0 # track how many times the Red/Yellow/Green status was updated.
Deep_probe = 0 # how many times was a deep probe required; Single_test_site ping test failed
Single_test_site = 0 # used to select non deep probe ping test.  Rotates thru the 10 IPs

Last_Red = "none detected"  # timestamps for when condition happened for screen report
Red_Event_Counter = 0       #also count number of time condition occured
Last_Yellow = "none detected"
Yellow_Event_Counter = 0
Last_Green = "none detected"
Green_Event_Counter = 0

import os # allows for the ping calls

import RPi.GPIO as GPIO  # We will be using the RasPI GPIO to control the three LEDss
GPIO.setwarnings(False) # suppress screen from showing GPIO errors

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)
# set up GPIO output channel
# I/O 15 = RED
# I/O 18 = YELLOW
# I/O 22 = GREEN
# NOTE: LOGIC LOW TURNS ON THE LEDs (they are active LOW)
GPIO.setup(15, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)

#Turn off all the LEDs
GPIO.output(15,GPIO.HIGH) 
GPIO.output(18,GPIO.HIGH)
GPIO.output(22,GPIO.HIGH)

SiteStatus = [1,2,3,4,5,6,7,8,9,10,11]  # Pass/Fail of the site ping
UpTally = [1,2,3,4,5,6,7,8,9,10,11]    # count succesful pings for each site
PingCount = [1,2,3,4,5,6,7,8,9,10,11]  # count how many times each site is pinged
PingSite = [1,2,3,4,5,6,7,8,9,10,11]   # holds the IP or DNS name of the site being pinged

# Identify ten PING 'friendly' sites
PingSite[1] = "pingfriendlysite-1.com"
PingSite[2] = "pingfriendlysite-2.com"
PingSite[3] = "pingfriendlysite-3.com"
PingSite[4] = "pingfriendlysite-4.com"
PingSite[5] = "pingfriendlysite-5.com"
PingSite[6] = "pingfriendlysite-6.com"
PingSite[7] = "pingfriendlysite-7.com"
PingSite[8] = "pingfriendlysite-8.com"
PingSite[9] = "pingfriendlysite-.9com"
PingSite[10] = "pingfriendlysite-10.com"

print "  " # print startime, self test, etc...
print time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "-------------------------"
print "LED Self Test in process..."
print "  "
print "Test Pings Sites are:"
print "-------------------------"
for checksite in range (1,11): #init some vars, display sites on screen during self test.
    UpTally[checksite] = 0 # if site is UP when tested then incremented
    PingCount[checksite] = 0 # if site is tested then increment
    SiteStatus[checksite] = "---"
    print checksite, PingSite[checksite]
   
# Self test the LEDs by blinking them.  Remember; active LOW.
st_delay = 0.20
for st in range (0, 5): 
    GPIO.output(15,GPIO.LOW) # Red
    time.sleep(st_delay)
    GPIO.output(15,GPIO.HIGH)
   
    GPIO.output(18,GPIO.LOW) # Yellow
    time.sleep(st_delay)
    GPIO.output(18,GPIO.HIGH)

    GPIO.output(22,GPIO.LOW) #Green
    time.sleep(st_delay)
    GPIO.output(22,GPIO.HIGH)

print "  "
print "Test pinging all sites for first run..."
print "  "

# get a 1st run baseline so the status printout looks normal/clean
LED_status = 0  # LED_Status keeps score of the successful pings on the test sites
for checksite in range (1,11):
    # All LEDs on during the 1st run pings; flash effect
    print "  "
    print "All LEDs on..."
    GPIO.output(15,GPIO.LOW)
    GPIO.output(18,GPIO.LOW)
    GPIO.output(22,GPIO.LOW)
   
    hostname = PingSite[checksite]
    PingCount[checksite] = PingCount[checksite] + 1
    response = os.system("ping -c 1 " + hostname)
    if response == 0:
        #hostname 'is up!'
        SiteStatus[checksite] = "UP"
        LED_status = LED_status + 1
        UpTally[checksite] = UpTally[checksite] + 1
    else:
        #hostname 'is down!'
        SiteStatus[checksite] = "DOWN"
       
    # All LEDs off; flash effect
    print "  "
    print "All LEDs off..."
    GPIO.output(15,GPIO.HIGH)
    GPIO.output(18,GPIO.HIGH)
    GPIO.output(22,GPIO.HIGH)
    time.sleep(.5) # little delay for the LED off

print "  "
print "Self Test complete..."
print "  "

while True: #Loop forever to check and display ping test status
    Loop_count = Loop_count + 1
    # Only test ping all sites if the one site tested sites fails the ping
    # Here we test ping a single site.  Next time we ping test the next site on our list of 10.
    if Single_test_site < 10:  # single test site selection
        Single_test_site = Single_test_site + 1
    else:
        Single_test_site = 1
       
    hostname = PingSite[Single_test_site] # OK, now the have selected one of our ping test sites
    print "Ping site: " + hostname        # so let's test ping it with os.system command
    print "  "
    time.sleep(1.5)
    PingCount[Single_test_site] = PingCount[Single_test_site] + 1
    response = os.system("ping -c 1 " + hostname)
    if response == 0:
        # tested site is up.  all is fine.  don't deep probe, but update the tally stats
        SiteStatus[Single_test_site] = "UP"
        UpTally[Single_test_site] = UpTally[Single_test_site] + 1
        Led_status = 10 # insure GREEN condition
    else: # If selected site is down, so now we call for a vote by testing all sites. Let's deep probe.
        print "  "
        print "Selected site failed ping test.  Intiating deep probe..."
        print "  "
        Deep_probe = Deep_probe + 1
        LED_status = 0
        for checksite in range (1,11):
            hostname = PingSite[checksite]
            PingCount[checksite] = PingCount[checksite] + 1
            response = os.system("ping -c 1 " + hostname)
            if response == 0:
                #hostname 'is up!'
                SiteStatus[checksite] = "UP"
                LED_status = LED_status + 1
                UpTally[checksite] = UpTally[checksite] + 1
            else:
                #hostname 'is down!'
                SiteStatus[checksite] = "DOWN"
        time.sleep(.1)
   
    #Adjust the LED status light and screen print a report
           
    if (LED_status >= 7): #Hurray, looking good. Turn on Green LED.  All others off
        GPIO.output(15,GPIO.HIGH) #Red OFF
        GPIO.output(18,GPIO.HIGH) #Yellow OFF
        GPIO.output(22,GPIO.LOW) #Green ON
        Status_Color = "GREEN"
        Last_Green = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        Green_Event_Counter = Green_Event_Counter + 1
       
    if 3 < LED_status < 7:  #Things are shakey.  Yellow LED on.  All others off
        GPIO.output(15,GPIO.HIGH) #Red OFF
        GPIO.output(18,GPIO.LOW) #Yellow ON
        GPIO.output(22,GPIO.HIGH) #Green OFF   
        Status_Color = "YELLOW"   
        Last_Yellow = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        Yellow_Event_Counter = Yellow_Event_Counter + 1
       
    if (LED_status <= 3): #Oh no, looking bad. Turn on Red LED.  All others off
        GPIO.output(15,GPIO.LOW) #Red ON
        GPIO.output(18,GPIO.HIGH) #Yellow OFF
        GPIO.output(22,GPIO.HIGH) #Green OFF
        Status_Color = "RED"       
        Last_Red = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        Red_Event_Counter = Red_Event_Counter + 1
   
    os.system('clear')  # clear the screen for the report
    print "  "
    print time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) + ".  Update interval: " + str(Delay_between_tests) + " seconds."
    runtime = datetime.datetime.now() - t0
    print "Status report: " + Status_Color + ".  Runs: " + str(Loop_count) + ".  DeepProbes: " + str(Deep_probe) + ".  Runtime: " + str(runtime)
    print "--------------------------------------------------------------------------"
    for checksite in range (1,11):
        if Single_test_site == checksite:
            LastCheck = ">>>Last check:"
        else:
            LastCheck = "   Last check:"
    Red_print =    "     RED:" + "%6s" % str(Red_Event_Counter)    + " events.  Last event: " + Last_Red
    Yellow_print = "  YELLOW:" + "%6s" % str(Yellow_Event_Counter) + " events.  Last event: " + Last_Yellow
    Green_print =  "   GREEN:" + "%6s" % str(Green_Event_Counter)  + " events.  Last event: " + Last_Green
  
    print "--------------------------------------------------------------------------"
    print "  "
    print Red_print
    print Yellow_print
    print Green_print
    print " "
  
    # At each hour trigger IFTTT and update Google Drive Spreadsheet.  Will always trigger on 1st run
    Current_hr = time.strftime("%H", time.localtime())
    if (Current_hr != IFTTT_hr):
        IFTTT_hr = Current_hr
        url = url = "https://maker.ifttt.com/trigger/trigger_stat/with/key/" + MAKER_SECRET_KEY + "?value1=" + Red_print + "&value2=" + Yellow_print + "&value3=" + Green_print
        res = requests.get(url)
        #print str(res) + " is web request result for BT device: "   #used only for debug
        time.sleep(5)

    time.sleep(Delay_between_tests) #wait some before next ping test
 -----
If you're still with us, thanks and if you duplicate the build let us know!