Wednesday, February 26, 2020
ESP8266 Doorbell Sends SMS and Updates Google Sheet
On-line shopping has changed the way goods are acquired. For most packages it is not necessary to be around for the dropoff. But... for the important stuff it is. Those packages always seem to show up right when you are in the backyard for mere seconds causing that frustrating "Personal Signature Required for Delivery" note on the door. This is our DIY effort to solve that problem.
-----
Needed:
- ESP8266 (microcontroller with WiFi)
- 1M ohm resistor
- reed switch
- IFTTT account (using the WebHook applet)
- wire and stuff
- fundamental knowledge on Arduino sketches (simple code edits).
-----
The connections are simple:
----
And the rig will look something like this:
-----
A 1M ohm resister connected from 3.3V to the A0 [ADC] input on the ESP8266 keeps the A0 reading at maximum value. The normally open reed switch is placed right on top of the coils that activate the doorbell. These coils act like that electromagnet you build in grade school to ring the doorbell and cause the reed switch to close. This drops the impedance to the 1M resistor and changes the ADC value on the A0 pin for detection of the doorbell. Then the SMS is sent to your phone and a Google Drive Sheet is updated.
-----
-----
The sketch you need to upload to the the ESP8266 looks like this:
```````````````````````````````````````````````````````````````````````````````
/*
* FEB2020
* Door Bell Monitoring
* WhiskeyTangoHotel.Com
*
* Build details at:
* http://www.whiskeytangohotel.com/2020/02/esp8266-doorbell-sends-sms-and-updates.html
*
* On Doorbell ring:
* Logs to Google Drive (as an appending spreadsheet)
* Sends SMS to cell phone
*
* ESP8266 uC
*/
// For the Wireless
#include <ESP8266WiFi.h>
// WiFi Connection Information
const char* ssid = "yourssid"; // PRIVATE: Enter your personal setup information.
const char* password = "yourwifipassword"; // PRIVATE: Enter your personal setup information.
// IFTTT Information for WebHook widget
String MAKER_SECRET_KEY = "yourIFTTTprivatekey"; // PRIVATE: Enter your personal setup information. Your IFTTT Webhook key here
String TRIGGER_NAME_google_drive = "googlebell"; // this is the Maker IFTTT trigger name for google drive spreadsheet logging
String TRIGGER_NAME_SMS = "doorbell"; // this is the Maker IFTTT trigger name to send SMS.
const char* host = "maker.ifttt.com";
String url_SMS; // url that gets built for the IFTTT Webhook sending SMS
String url_google_drive; // url that gets built for the IFTTT Webhook logging to google drive spreadsheet
String Status ="**_Starting_on:"; // Status payload for Google Sheet. We log all starts and reboots
// Define and set up some variables
int sensorValue = 0; // reading from A0. This pin detects the doorbell (ADC reading between 0-1024)
// Define pins
const int led = 2; // Blue on board LED is on PIN2. Active LOW. Blinks it between reads
const int bell = A0; // analog input for the doorbell transducer. 1MOhm on board in // with 1MOhm from the reed switch pickup
// Program control variables
int Seconds_dwell_after_detect = 8; // Prevents logging flood and debounce. Sensor reads, LED flashing, etc.
int logging = 1; // If 1 then SMS and log to cloud. Any other value (0) turns it off.
void setup(){ // This is run once.
pinMode(led, OUTPUT); // set up the onbaord LED pin as an output.
Serial.begin(115200); // turn on the serial monitor for debug
// wait until serial port opens for native USB devices
while (! Serial) {
delay(10);
}
// Is the WiFi working?
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Trying to connect to ");
Serial.print(ssid);
Serial.print(" on ");
Serial.println(WiFi.localIP());
for (int x = 0; x < 20; x++) { //
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED. Shows program is trying to WiFi connect
//Serial.println("Server Start blink loop....");
delay(5); // Delay so short the LED looks like it is always on
} // endfor WiFi blink connect
}
Serial.print("Connected to ");
Serial.println(ssid);
Serial.println(WiFi.localIP());
for (int x = 0; x < 10; x++) { // 5 slow LED blinks to slow WIFI Connected.
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED.
Serial.println("WIFI is Connected....");
delay(500); } // endif for WIFI Connected blink
// Use WiFiClient class to create TCP connections for WiFi logging
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed"); // Boo!!!
return;
}
// Trigger the IFTTT Webhook Channel to update a Google sheet with the activity of the server starting/restarting
// This can help log power outs, etc. For first run we defined String Status for identify a startup condition.
// Create the request for IFTTT google drive
url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Status);
Serial.println (url_google_drive);
Serial.println(" ");
// This sends the request to the IFTTT server
client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(500); // Delay for web traffic; maybe not required.
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r'); }
String Status = "Payload_String_for_Google_Sheet";
}
void loop(){ // Loop forever or until the Dallas Cowboys win a Super Bowl
// Read the A0 pin and post based on the delay values set above.
// The blue onboard LED will fast blink while polling
sensorValue = analogRead(bell);
//Serial.println (sensorValue); //debug only
// Fast Blink Blue LED while waiting for doorbell press
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED. Shows program is running
Serial.println("Waiting for DoorBell....");
delay(50);
// Use WiFiClient class to create TCP connections for IFTT
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
if (sensorValue < 200 || sensorValue > 300) { // A0 values between 200-300 are normal. Increases (apprx doubles) when DB is pressed
Serial.println("----------Doorbell Dectected----------");
if (logging == 1) { // is logging turned on? Non "1" is for debug... Typically would be set = 1
Serial.println("***** Logging is ON *****");
// Build the IFTTT Webhoo Channel for SMS url
url_SMS = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_SMS + "/with/key/" + MAKER_SECRET_KEY;
client.print(String("POST ") + url_SMS + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
Serial.println(" ");
Serial.println("SMS payload to IFTTT is:");
Serial.println(url_SMS);
Serial.println(" ");
// This must be run before any IFTTT webhook
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
// Set up IFTTT Webhook Channel to update a Google sheet with the activity.
Status ="__Doorbell_on:";
url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Status);
client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
Serial.println(" ");
Serial.println("IFTTT url payload to Google Drive is:");
Serial.println(url_google_drive);
Serial.println(" ");
} else { // We are not sending to IFTTT. Debug mode
Serial.println("Logging is OFF.");
Serial.println(" ");
} // endif/else logging
for (int x = 0; x < Seconds_dwell_after_detect*2; x++) { // DoorBell dectected. Pause and LED flash for visual acknowledgement
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED
Serial.println("Doorbell dectected, Now in BLINK Loop....");
delay(500); } // endfor DoorBell Dwell loop
} // end doorbell A0 pressed
} // end of endless loop
-----
That's it and thanks for stopping by!!!
Thursday, August 24, 2017
ToF Laser to Monitor Cat Food Levels
We live in great times. There was once a day when the only way to determine if the cat feeder needed more kibble was to actually look at it with your own eyes like some Neanderthal. Thankfully technology and the magical world of IoT has changed all of that.
----
For this project we use a WiFi enable ESP8266 and a STMicro VL53L0X ToF Sensor mounted to a breakout board. If you don't find a need to measure feline food consumption the project still provides Arduino code that can be extremely useful for your other IoT projects:
- Logging data to a Google Drive Spreadsheet (via IFTTT)
- Logging data to AT&T's M2X machine to machine servers (think nice graphs)
- Sending SMSs to your mobile device (via IFTTT)
----
What's happening?
The ESP8266 runs in Arduino mode (source code below) in an endless loop. Every hour it polls the VL53L0X ToF Sensor mounted on the lid of the cat food feeder.
-----
Since we know how many centimeters the food is from the ToF sensor at full and at empty we are able to scale those values and report/log "percent full" status. Those values are posted to a Google Drive Spreadsheet and to AT&T's M2X machine to machine servers for logging. If the food level is considered CRITICALLY LOW a SMS message goes out to our mobile device. Just to increase the geek factor, CRITICALLY LOW alerts are also displayed on our Pebble watch.
----
The Google Drive Spreadsheet looks like this:
-----
Here is the bad ass AT&T M2X machine to machine server graph (note the increase after we filled the feeder):
-----
The IFTTT SMS alerts are sent if the rig determines food levels critically low:
-----
So..... How's it done? You will need these:
- WiFi enable ESP8266
- STMicro VL53L0X ToF Sensor mounted to a breakout board
- Some wire and a breadboard.
And it will look something like this:
-----
Mount it to the cat feeder and you end up with this:
-----
Now all that is left is to copy/paste the code below into the Arduino IDE. Upload it to the ESP8266 and your cats will never go hungry again!
-----
/*
* AUG2017
* STMicro VL53L0X ToF Sensor for
* Cat Food Level Monitoring
* WhiskeyTangoHotel.Com
*
* Logs % full values to:
* Google Drive (as an appending spreadsheet)
* AT&T M2X for historical graphing
* Send SMS to cell phone if level condition is RED/CRITICAL
*
* uC setting for Ardunio IDE
* NoderMCU 1.0 (ESP-12E Module), 80MHz, 921600, 4M (3M SPIFFS)
*
*/
// For the STMicro VL53L0X ToF Sensor
// I2C SDA to ESP8266 Pin D2. SCL to ESP8266 Pin D1
#include "Adafruit_VL53L0X.h" // Thanks again ADAFRUIT!!!
Adafruit_VL53L0X lox = Adafruit_VL53L0X();
// For the Wireless
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
// WiFi Connection Information
const char* ssid = "YourNetworkHere"; // PRIVATE: Enter your personal setup information.
const char* password = "YourNetworkPasswordHere"; // PRIVATE: Enter your personal setup information.
ESP8266WebServer server(80);
// IFTTT Information for WebHook widget
String MAKER_SECRET_KEY = "YourIFTTTCodeHere"; // PRIVATE: Enter your personal setup information. Your IFTTT Webhook key here
String TRIGGER_NAME_google_drive = "Cat_Food"; // this is the Maker IFTTT trigger name for google drive spreadsheet logging
String TRIGGER_NAME_M2X = "cat_food_mx"; // this is the Maker IFTTT trigger name for M2X logging
String TRIGGER_NAME_SMS = "CriticalFoodLevel_SMS"; // this is the Maker IFTTT trigger name to send SMS if low is level is CRITICAL.
const char* host = "maker.ifttt.com";
String url_google_drive; // url that gets built for the IFTTT Webhook logging to google drive spreadsheet
String url_M2X; // url that gets built for the IFTTT Webhook logging to AT&T M2X service
String url_SMS; // url that gets built for the IFTTT Webhook sending SMS if food level is critical
// Define and set up some variables
float Range_inches; // How far the sensor is from the food at time of reading. Sensor is on roof of feeder.
float Min_level = 5.0; // Distance in inches before low food alarm level. Food is far from sensor on feeder roof
float Max_level = 0.5; // Distance in inches from sensor for full feeder level. Food is close to sensor on feeder roof
float Percent_full; // How full in xx.x% is the food based on the Min/Max_levels defined above
float Caution_alarm = 35.0; // at xx.x% food level is considered low. String Status YELLOW
float Critical_alarm = 25.0; // at xx.x% food level is considered critically low. String Status RED
String Status = "***_Starting_with_Caution_at:_" + String(Caution_alarm) + "%_and_CRITICAL_at:_" + String(Critical_alarm) + "%"; // Update to Out of Range, NORMAL, LOW, CRITICAL, etc. "spaces" will error IFTTT Webhook; use "_"
int Run_number; // how many times the sensor has been read
// Output pins
const int led = 2; // Blue on board LED is on PIN2 for this NoderMCU 1.0 ESP8266. Blink it between reads
// Program control variables
int Seconds_between_posts = 60 * 60; // how often to post the results of the sensor read. NOT EXACT due to post delays, Sensor reads, LED flashing, etc.
int logging = 1; // If 1 then log to cloud. Any other value (0) turns it off. ESP8266 "Start/Restart" message is always logged.
void setup(void){ // This is run once.
pinMode(led, OUTPUT); // set up the onbaord LED pin as an output.
Serial.begin(115200); // turn on the serial monitor for debug
// wait until serial port opens for native USB devices
while (! Serial) {
delay(1);
}
// Is the ToF sensor connecting via I2C?
Serial.println("STMicro VL53L0X test");
if (!lox.begin()) {
Serial.println(F("Failed to boot VL53L0X!!!"));
while(1);
}
// power
Serial.println(F("VL53L0X Passed... \n\n"));
// Is the WiFi working?
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.print("Connected to ");
Serial.println(ssid);
Serial.println(WiFi.localIP());
if (MDNS.begin("esp8266")) {
Serial.println("MDNS responder started");
Serial.println(" ");
}
// Use WiFiClient class to create TCP connections for WiFi logging
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed"); // Boo!!!
return;
}
server.begin();
Serial.println("HTTP server started"); // Woo Hoo!!!
// Write to Google Sheet via IFTTT Maker channel that the ESP8266 has started/restarted
// Trigger the IFTTT Webhook Channel to update a Google sheet with the activity of the server starting/restarting
// This can help log power outs, etc. For first run we defined String Status for identify a startup condition.
// Create the request for IFTTT google drive
url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Status);
Serial.println("Status: " + Status);
Serial.println(" ");
// This sends the request to the IFTTT server
client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(500); // Delay for web traffic; maybe not required.
}
void loop(void){
// Loop forever. Read the sensor and post based on the delay values set above.
// The blue onboard LED will blink between ToF reads.
// Read the ToF. Distance in mm returned.
for (int x = 0; x < 10; x++) { // Quick toggle blue on board LED to show measurement being taken.
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED. Shows program is running
delay(100); } // endfor quick toogle Blue LED
VL53L0X_RangingMeasurementData_t measure;
Serial.println("-------------------------------------");
Serial.println("Reading a measurement... ");
lox.rangingTest(&measure, false);
// Convert to inches because the USA, for some reason, doesn't want to adopt the metric system...
Range_inches = measure.RangeMilliMeter/25.4;
Run_number = Run_number + 1;
// Scale and normalize the Min Max levels to 0% to 100%. Clip the range in case of a the Max fill limit was exceeded or a misread.
Percent_full = ((Range_inches - Min_level) / (Max_level - Min_level)) * 100.0;
if (Percent_full > 100) {
Percent_full = 100.00;
}
if (Percent_full < 0) {
Percent_full = 0.0;
}
//Serial.println(String(Range_inches)); // Debug use
// Is the ToF Sensor reading 'anything' for a distance?
if (Range_inches > 100 ) { // Something's weird. Ranging error. The ToF sensor is NOT over 100 inches fron the food. EVER!!!
Status = "Run:" + String(Run_number) + "__ERROR:_***_Out_of_Range_***";
} else { // ToF made a successful reading so log the food level
if (Percent_full >= Caution_alarm) { // above CAUTION LEVEL, All's good
Status = "Run:" + String(Run_number) + "___GREEN---GOOD";
} // endif GREEN---GOOD
if (Percent_full < Caution_alarm && Percent_full > Critical_alarm) { // CAUTION Zone, YELLOW---REFILL_SOON
Status = "Run:" + String(Run_number) + "___~~~YELLOW---REFILL_SOON~~~";
} // end if YELLOW---REFILL_SOON"
if (Percent_full <= Critical_alarm) { // CAUTION Zone, RED---REFILL_ASAP"
Status = "Run:" + String(Run_number)+ "___!!!_RED---REFILL_ASAP_!!!";
if (logging == 1) { // is logging turned on? Maninly for debug... Typically would be set = 1
// Set up IFTTT Webhook Channel to send the SMS.
// Use WiFiClient class to create TCP connections for IFTT SMS
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
url_SMS = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_SMS + "/with/key/" + MAKER_SECRET_KEY+ "?value1=" + String(Percent_full);
Serial.println("Critical Level: Sending SMS with payload:.");
Serial.println(url_SMS);
Serial.println(" ");
client.print(String("POST ") + url_SMS + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
//Serial.println("GOOGLE DRIVE URL:");
//Serial.println(url_google_drive);
delay(500); // pause for webservices
}
} // endif RED---REFILL_ASAP
}
// Serial print to the monitor for debug
Serial.println(String(Range_inches) + " inches down / " + String(Percent_full) + "% full");
Serial.println("Status: " + Status);
// Create the request for IFTTT Google Drive and M2X updates
url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Percent_full) + "%" + "&value2=" + Status;
url_M2X = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_M2X + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Percent_full);
// This sends the request to the IFTTT server.
//Serial.println("Requesting URL..."); // debug
//Serial.println(url); // debug
if (logging == 1) { // is logging turned on? Maninly for debug... Typically would be set = 1
//Serial.println("Logging is ON.");
// Set up IFTTT Webhook Channel to update a Google sheet with the activity.
// Use WiFiClient class to create TCP connections for IFTT Webhook logging
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
Serial.println(" ");
Serial.println("IFTTT url payload to Google Drive:");
Serial.println(url_google_drive);
Serial.println(" ");
delay(500); // pause for webservices
// Set up IFTTT Maker Channel to update a AT&T M2X Server with the activity.
// Use WiFiClient class to create TCP connections for IFTT Webhook logging
//WiFiClient client;
//const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return; }
client.print(String("POST ") + url_M2X + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
Serial.println("IFTTT url payload for M2X:");
Serial.println(url_M2X);
} else {
Serial.println("Logging is OFF.");
} // endif/else logging
for (int x = 0; x < Seconds_between_posts; x++) { // Delay for next measurement.
digitalWrite(led, !digitalRead(led)); // toggle state of the on board blue LED. Shows program is running
delay(1000); } // endfor delay for read measurement
}
-----
Thanks and check out our other stuff!!!
Monday, May 22, 2017
Tweeting Cat Door (now with Rasperry PI)
It is hard to believe, but it was over five years ago that we created a Tweeting CatDoor using the Electric Imp. Basic hobby IoT was much harder back then. The Electric Imp, being a commercial grade product, was rock solid. It only needed to reboot twice during it's entire service. That said, a lightening strike may have gotten the best of it.
----
Basic hobby IoT is easier today thanks to project sites like HackaDay, etc.
A look into our spare parts box prompted some changes: The Electric Imp gets replaced by a Raspberry Pi ZERO and WiFi dongle (we didn't have a spare ZERO-W laying around). The original magnetic reed switch implementation goes solid state and gets replaced with a A3144EUA Hall Effect Sensor. The concept is still the same as the original; a magnet is placed on the door and when Zena or Kelso use the door a magnet swings past the stationary sensor triggering a GPIO event.
As shown in the wiring diagram above, the build is simple. The python code straightforward.
For the python code below you will need you create your own Twitter App and establish:
# Raspberry PI ZERO for tweeting Cat Door activity
# MagSwitch is model A3144EUA Hall Effect sensor with magnet next to it
# Activate tweet loop whenever the Hall Effect Sensor changes state
import random
import time
# Set up Auth and token ID for the twitter API
from twython import Twython
# fill in your 4 keys in following variables
C_key = "ckckckckckblaghblagckckckckck"
C_secret = "cscscscscsblaghblaghblaghcscscscscs"
A_token = "atatatatatblaghblaghblagatatatatat"
A_secret = "asasasasasblaghblaghblagasasasasas"
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
Current_day = time.strftime("%d", time.localtime()) # what day of the month is it
New_day_dawned = Current_day # 1st run after midnight send status tweet to verify program is running
### PROGRAM VAR SETTINGS ###
# Flood control (in seconds) for debounce and not sending to many tweets
Flood_control = 180 # seconds
# Really tweet (YES) or just test/debug (NO)?
Tweet = "YES" # YES or NO in ALL UPPERCASE
# Track how many times the door has been triggered
Counter = 0
# Set up Hall Effect Sensor
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD) # to use Raspberry Pi board pin numbers
MagSwitch = 8 # Pin from signal of hall effect sensor
#Set pin to input and set pull-up resistor to hold the pin is high
GPIO.setup(MagSwitch, GPIO.IN, pull_up_down=GPIO.PUD_UP)
print ' '
print "Program starts with Flood control of " + str(Flood_control) + " seconds."
print "Tweet set to " + Tweet
print " "
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "RasPI started on " + localtime + ". Begin the mission!!!"
MagSwitch_Current = GPIO.input(MagSwitch)
#print "MagSwitch value is: " + str(MagSwitch_Current)
print ' '
if (Tweet == "YES"):
myTweet = Twython(C_key,C_secret,A_token,A_secret)
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
myTweet.update_status(status="RasPI started at " + localtime + ". Begin the mission!!!")
time.sleep(1) # Whew... let's rest a bit.. Why? Dunno...
while True: # program runs 'forever'
# First check if this is a new day. If yes, tweet program is still active
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
Current_day = time.strftime("%d", time.localtime())
if Current_day != New_day_dawned:
print "A new day has dawned. It is " + localtime + "."
print ' '
if (Tweet == "YES"):
myTweet = Twython(C_key,C_secret,A_token,A_secret)
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
myTweet.update_status(status="Hello, Twitter. Let's make the best of " + localtime)
New_day_dawned = Current_day
# Now check to see if Sensor has changed state. If so then Let's tweet
if (GPIO.input(MagSwitch) != MagSwitch_Current):
print ' '
localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "Triggered at: " + localtime
print '-------------------------------'
Counter = Counter + 1
# Load the CatQuotes; then Tweet it
CatQuote = [
"ZERO", #this is zero and should never be called. just here to make counting easier 'cuz i'm stupid
"Tweet Quote 1",
"Tweet Quote 2",
"Tweet Quote 3",
"Tweet Quote 4",
"Tweet Quote 5",
"Tweet Quote etc...", ]
Number_of_quotes = 6 - 1
random.seed() # seed off the CPU clock
r = random.random()
r = int((r * Number_of_quotes) + 1)
print str(Counter) + ": CatQuote("+str(r)+"): " + CatQuote[r]
print "Flood control set to " + str(Flood_control) + " seconds."
print "Tweet set to: " + Tweet
if (Tweet == "YES"):
myTweet = Twython(C_key,C_secret,A_token,A_secret)
myTweet.update_status(status=CatQuote[r])
time.sleep(Flood_control) # pause for debounce, re-read HE sensor, keeps from tweeting all the time.
# Door etc has settled after the Flood Control. Reset and wait for next toggle
MagSwitch_Current = GPIO.input(MagSwitch)
print "Flood Control PASSED."
print " "
Friday, January 6, 2017
ESP8266 WiFi Garage Door Opener from any Web Browser
-----
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, 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.
-----
-----
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)
-----