Automatic Thermostat Control Based on Location and Weather

The Pittsburgh Perl Workshop will be held at the Carnegie Mellon University on October 9-10, 2010. The PPW is a gathering of Perl programmers from around the world (and near Pittsburgh) to learn more and discuss the future of Perl.

At this year’s PPW, I will be giving a talk called, “Connecting the Internet of Things with Perl“ (visit pghpw.org for schedule info). I will also explain how to create an Internet of Things application using off-the-shelf Perl modules and web control technology by ioBridge.

As you may or may not know, Perl is a really powerful programming language that enables everything from fast prototyping of web applications to large-scale software platforms. What makes the language unique is the library of modules available to you. If you get a great new idea for a web app, you can get started quickly and find modules that others have written. In some cases, it’s literally copy-and-paste.

A big movement for the past few years is this concept of The Internet of Things. More things will be on the Internet than people in the next few years, so my talk is to highlight why Perl is still relevant after 20 years and needs to be apart of this emerging technology. Internet of Things applications involve connecting sensors and controllers to the web. Perl is perfect for parsing lots of data, pushing data into databases, and connecting services together, known as “mashups”.

My Internet of Things project, written in Perl, allows your current location and home weather conditions to control your home heating and cooling system.

Location Aware Home Automation using Google Latitude API and ioBridge API

I call it,  ”Location Aware Home Automation”. You don’t have to do anything to control your HVAC/Thermostat, it all happens based on where you are. If you are home, the thermostat regulates the inside temperature as normal. When you leave, systems turn off or enter power saving modes. When you get near your home, the heating/cooling system kicks back on so you have a comfortable temperature by the time you get back home. In order to pull off all of this passive and automatic functionality, I have mashed up several APIs from Google Latitude, WeatherBug, and ioBridge.

Using the API for Google Latitude, I track the location of my Android mobile phone. When I get near my home, I check the weather using Google Weather API, WeatherBug API, and my home temperature (via ioBridge) to see if I need to to use the air conditioner, the heater, or neither. If I do need to control the HVAC, I send the control commands using the ioBridge API that routes the commands to the IO-204 controller that’s hooked up to my thermostat.

This application is really just a beginning. Right after I got everything working, I started having a flood of ideas. I can see some real power here.

The ‘How To’ Portion of the Show

Google Latitude

You have to enable Google Latitude on your mobile phone and get your Badge ID. This ID represents your position in the world, your latitude and longitude. Visit the Google Latitude API site for more information.

Install the latest Geo::Google::Latitude Perl module from CPAN.org – this module completely abstracts the access to the Google Latitude API for you. All you have to do us pass your ID and the module returns the date, time, last known latitude and longitude (the values are in decimal degrees).

use Geo::Google::Latitude;
my $gl=Geo::Google::Latitude->new;
my $id="7832225593622256926";
my $badge=$gl->get($id);
my ($lat2, $lon2) = $badge->point->latlon;

Calculating how far you are away  from home

You have to figure out how far you are from home, you do this by doing some math. Oh wait, there’s a Perl module for that. Install Geo::Distance and all you have to do is tell it what latitude and longitude to compare and it spits out the distance.

use Geo::Distance;
my $geo = new Geo::Distance;
### Home Location
my $lon1 = "-79.76408";
my $lat1 = "39.980342";
### Calculated Distance
my $distance = $geo->distance( 'mile', $lon1, $lat1 =>; $lon2, $lat2 ); # Use 'meter' to calculate distance in meters

Getting the Weather

You can use a number of weather APIs to get weather data for your home location. All you need to know is where you live. The easiest to implement is Google Weather (Weather::Google), but the WeatherBug API has a lot more information you can use for other Internet of Things things you may do.

 use Weather::Google;
my $gw = new Weather::Google(15401); # Zipcode
my $current_outside = $gw->current->{temp_f}; #Use temp_c for Celsius

Connect to ioBridge

All you have to do to connect with ioBridge is to send command via the ioBridge Widget API. First you create the control widgets for your heating and cooling system. For mine, I can use relays. Others may need serial strings, which you can send as well. Once you have the widgets created, locate there widget ID’s and send them to the API.

use LWP::Simple;
my $Air_Conditioner_widgetID = "Gb2Q1FUKPmzZ"; ### Replace with your widget ID's
my $Heater_widgetID = "9c3WEGHKemnzJ";
my $Inside_Temp_widgetID = "D32SDghy98iOu";
my $ioBridgeAPI = "";
$ioBridgeAPI = "http://www.iobridge.com/widgets/static/id=" . $Inside_Temp_widgetID . "&value=1&format=text";
my $current_inside = get($ioBridgeAPI);
### Test if the heater or the air condition should be turned on
if ($current_outside >= 78 && $current_inside >= 72) {
$ioBridgeAPI = "http://www.iobridge.com/widgets/static/id=" . $Air_Conditioner_widgetID . "&value=1&format=text";
get($ioBridgeAPI);
}
elsif ($current_outside $ioBridgeAPI = "http://www.iobridge.com/widgets/static/id=" . $Heater_widgetID . "&value=1&format=text";
get("$ioBridgeAPI");
}

Putting it all together

Once you have the entire built all you have to do is call the app periodically using CRON Linux or Task Scheduler on Windows. Here is a TXT file of the Perl application with all of the parts tied together, probably will be easier to read and understand.

The hardware side uses the ioBridge IO-204 connected to the control lines of a thermostat or an HVAC control box. The lines switch at 12 volts, so I use relays trigger them. Other thermostats that I researched use serial lines which the IO-204 can tap into using RS-232.

It may seem like a lot of work, but just think about what is happening. Feeds from Google Latitude and WeatherBug are being processed and passed to your home network via the Internet. All of this is happening without your direct interaction – your things are working for you. I hope that you can see that is a start of some pretty amazing applications of technologies that will advance over time. A lot has changed in the past year, I can’t image what comes next.

If you get around to building a project like this, please drop me a line. I love this stuff.

New Google Search: Instant Narcissism

If you use Google Search as a lot of people do, then you have noticed the new feature from Google. As you type you get instant search results. It is an interesting feature and I am not sure how much this will change my search patterns. I still want to hit the return button after I type in a search phrase.

For the vain, the new Google Search will allow you to “google” yourself instantly.

Hans Scharler Google Search Vain

Internet-enabled Message Center

What are you up to now?

I took the leap and bought an Arduino from LiquidWare. An arduino is an open-source microcontroller that has a processor, some digital I/O pins, and analog inputs. You can create little standalone programs that monitor inputs, control LEDs, and pretty much anything that you dream up. My favorite projects are ones that involve the Internet. A microcontroller is rather simple by itself, but what if it could use the web to get answers, send email, maybe update my Twitter status? That means there is a unlimited number of projects ahead – Microcontrollers collaboarating in cyberspace. The missing link for the web part is the ioBridge IO-204. I know you are no stranger to the the IO-204, but for those of you who have not heard. The IO-204 sits on my network and relays data from its channels to ioBridge.com servers and back into my network. It allows for remote control and monitoring without network configuration and programming. One of the expansion boards is a two-way serial board that accepts serial strings and connects them to APIs of web services that ioBridge interfaces to and sends back responses. For instance, I can send the commands, “[[[calc|9*9]]]” and this returns 81. OK, maybe not impressive on the surface, but that result came from Google Calculator. Anything Google Calculator can solve, your microcontroller has access to those results. For more examples, visit the Serial Web Services API on the wiki.

Message Center Project

I wanted to combine these two worlds with a sample project – maybe it will inspire you to come up with something better, spark some ideas that you have. I have my arduino measuring my outside temperature here in Pittsburgh, which is an analog input scaled to Fahrenheit. At any moment I can press a button and get the temperature on the LCD screen – no Internet required. Since I have been planning a work trip to Atlanta, I also wanted to compare my temperature with hot-lanta’s. So, my project solves that. Using the “weather command”, I am able to get the weather anywhere in the world by zip code or city name.

I added a few more things to the message center. With another button I can get the stock quote of Google. My strike price was $405, so I have been watching it close. If it gets below $405, I get an automatic email from my message center. The stock quote comes from the Yahoo Financials API.

I have one more button that emails me a secret message when it’s pressed. I put this in here for when my mom comes into my room from when I am on the road. It’s aptly label, do not press. Next time, I will hook it to a light sensor in the basement to catch her when she turns on my lights. I am sure you all have the same issues with your mom.


Source Code

The arduino requires some c-like programming and I wanted to include the sketch for you to steal and use for your projects. You will see how I send the serial commands from the arduino to the IO-204 using the UART serial connection (pins 0/1) and recieve and parse the incoming results. I use a SoftwareSerial port for the LCD results. The push buttons are software debounced and use pull-up resistors for solid digital connections. The LED’s linked to each button use a 330 ohm resistor to protect them. I was aided by the Arduino Inputs tutorial on Ladyada.net, Debounce Tutorial, and the iobridge Wiki / Forum. Please let me know if you have any questions, maybe I can help. I have learned a lot about handling strings on the arduino.

//
// Message Center using Arduino and the ioBridge IO-204
//
// An open-souce Shadowlord Project
// www.IamShadowlord.com


#include SoftwareSerial.h>

// SoftwareSerial Pins
#define rxPin 2
#define txPin 3

// Setup Software Serial
SoftwareSerial
softSerial = SoftwareSerial(rxPin, txPin);

// Global Setup
int middleLED = 11;
int rightLED = 10;
int leftLED = 12;

int leftButton = 5;
int
leftButtonCurrent = LOW;
int leftButtonReading;
int leftButtonPrevious = HIGH;
long leftButtonTime = 0;
long
leftButtonDebounce = 200;

int middleButton = 4;
int middleButtonCurrent = LOW;
int middleButtonReading;
int
middleButtonPrevious = HIGH;
long middleButtonTime = 0;
long
middleButtonDebounce = 200;

int rightButton = 6;
int
rightButtonCurrent = LOW;
int rightButtonReading;
int
rightButtonPrevious = HIGH;
long
rightButtonTime = 0;
long
rightButtonDebounce = 200;

int tempPin = 5;
int tempAnalog = 0;
int
tempF = 0;

char* currentRequest = "";

// Start up program
void
setup() {

pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);

pinMode(leftLED, OUTPUT);
pinMode
(middleLED, OUTPUT);
pinMode(rightLED, OUTPUT);

pinMode(leftButton, INPUT);
pinMode(middleButton, INPUT);
pinMode(rightButton, INPUT);

softSerial.begin(9600);
delay(100);

Serial.begin(9600);
delay
(100);

Serial.flush();
delay(100);

// Setup LCD
clearLCD();
setBacklightBrightness(9);
delay
(1000);

// Test LEDs
digitalWrite
(leftLED, HIGH);
digitalWrite(middleLED, HIGH);
digitalWrite(rightLED, HIGH);

delay(1500);

digitalWrite(leftLED, LOW);
digitalWrite
(middleLED, LOW);
digitalWrite
(rightLED, LOW);

}

// Start main program loop
void
loop(){

// Get Analog Input and scale as temperature for ioBridge temperature sensor on arduino
tempAnalog = analogRead(tempPin);
tempF = tempAnalog / 6.875;

// Monitor left button status and debounce
leftButtonReading = digitalRead(leftButton);

if (leftButtonReading == HIGH && leftButtonPrevious == LOW &&
millis
() - leftButtonTime > leftButtonDebounce) {
if (leftButtonCurrent == HIGH) leftButtonCurrent = LOW;
else
{digitalWrite(leftLED, HIGH);
clearLCD();
delay
(100);
softSerial.print("Outside: ");
delay
(100);
softSerial.print(tempF);
delay
(100);
moveCursor("02", "01");
delay(100);
softSerial.print("Atlanta: ");
leftButtonCurrent = LOW;
//Request temperature in Atlanta via ioBridge
Serial.print("[[[weather|Atlanta]]]");
digitalWrite
(leftLED, LOW);
}
leftButtonTime = millis();
}

leftButtonPrevious = leftButtonReading;

// Monitor middle button status and debounce
middleButtonReading = digitalRead(middleButton);

if (middleButtonReading == HIGH && middleButtonPrevious == LOW &&
millis() - middleButtonTime > middleButtonDebounce) {
if (middleButtonCurrent == HIGH) middleButtonCurrent = LOW;
else
{currentRequest = "Google";
digitalWrite
(middleLED, HIGH);
clearLCD();delay(100);
softSerial.print("GOOG: $");
delay
(100);
middleButtonCurrent = LOW;
//Request Google Stock Price via ioBridge

Serial
.print("[[[stock|GOOG]]]");
digitalWrite
(middleLED, LOW);
}
middleButtonTime = millis();
}

middleButtonPrevious = middleButtonReading;

// Monitor right button status and debounce
rightButtonReading = digitalRead(rightButton);

if (rightButtonReading == HIGH && rightButtonPrevious == LOW &&
millis() - rightButtonTime > rightButtonDebounce) {
if
(rightButtonCurrent == HIGH) rightButtonCurrent = LOW;
else
{
digitalWrite
(rightLED, HIGH);
clearLCD();
delay(100);
softSerial.print("Alert: ");
delay
(100);
rightButtonCurrent = LOW;
//Send email via ioBridge

Serial
.print("[[[email|hans@nothans.com|Alert|Mom, is pressing your buttons]]]");
digitalWrite(rightLED, LOW);
}
rightButtonTime = millis();
}

rightButtonPrevious = rightButtonReading;

// Display serial messages
if(Serial.available() > 0){

delay(100);

char charIn = 0;
byte i = 0;
char
stringIn[32] = "";

while(Serial.available()) {
charIn = Serial.read();
stringIn[i] = charIn;
i += 1;
}

if (currentRequest == "Google") {

softSerial.print(stringIn);
int stockPrice = atoi(stringIn);
delay(100);
moveCursor("02", "01");
delay(100);
stockPrice = stockPrice - 405;
softSerial.print("Change: $");
delay
(100);
softSerial.print(stockPrice);
currentRequest = "";

}
else
softSerial.print(stringIn);
}

// End program loop
}

//
// ioBridge Serial LCD Functions and Parameters (for SoftwareSerial)
//

void displayMessage(char* message){
softSerial.print(message);
}

void clearLCD(){
softSerial.print(0xFE, BYTE);
softSerial.print("Z");
}

void setBacklightBrightness(int level){
// level
// 0=Off -> 9=Brightest

softSerial.print(0xFE, BYTE);
softSerial.print("B");
softSerial.print(level);
}

void setBacklightTime(int level, byte seconds){
// level
// 0=Off -> 9=Brightest

// seconds
// 01 = 1 seconds => 06 = 60 seconds


softSerial.print(0xFE, BYTE);
softSerial.print("T");
softSerial.print(level);
softSerial.print(seconds, BYTE);
}

void moveCursorHome(){
softSerial.print(0xFE, BYTE);
softSerial.print("H");
}

void turnCursorOn(){
softSerial.print(0xFE, BYTE);
softSerial.print("J");
}

void turnCursorOff(){
softSerial.print(0xFE, BYTE);
softSerial.print("K");
}

void turnBlinkingCursorOn(){
softSerial.print(0xFE, BYTE);
softSerial.print("P");
}

void turnBlinkingCursorOff(){
softSerial.print(0xFE, BYTE);
softSerial.print("Q");
}

void scrollMessage(int row, int speed, char* message){
// row
// 1=First Line -> 2=Second Line


// speed
// 0=Slowest -> 9=Fastest


softSerial.print(0xFE, BYTE);
softSerial.print("S");
softSerial.print(row);
softSerial.print(speed);
softSerial.print(message);
softSerial.print(0xFE, BYTE);
}

void moveCursor(char* row, char* column){
// row
// 01=First Line -> 02=Second Line

// column
// 01=First Position -> 16=Last Position


softSerial.print(0xFE, BYTE);
softSerial.print("L");
softSerial.print(row);
softSerial.print(column);
}

void drawHorizontalGauge(int row, char* leftLabel, char* rightLabel, char* length){
// row
// 1=First Line -> 2=Second Line

// leftLabel and rightLabel
// 2 character labels

// length
// a=Empty -> k=Full (filled in from left to right)


softSerial.print(0xFE, BYTE);
softSerial.print("G");
softSerial.print(row);
softSerial.print(leftLabel);
softSerial.print(rightLabel);
softSerial.print(length);
}

void drawVerticalGauge(int height){
// height
// 0=Bottom -> 8=Top (filled in from bottom to top)

softSerial.print(0xFE, BYTE);
softSerial.print("V");
softSerial.print(height);

}

Bonus Project

It’s simple, but I hacked together a power supply for the Arduino, which gets power from USB or a coaxial input from a transformer. I wanted to only run one brick, wall wart, so I hacked a USB cable. There are 4 wires in the USB cable (from pinouts.ru):

1 VCC Red +5 VDC
2 D- White Data –
3 D+ Green Data +
4 GND Black Ground

The IO-204 has a regulated 5VDC and ground (up to 1A – 4A total draw depending on supply) on each channel, so using a terminal strip, I connected the VCC and GND to a cut in half USB cable.

It’s magic – look ma, only one power source.