Introduction: Use Twitter and Weather to Post Notes and Temperature to Office Door

Synopsis
========
The goal of this project is to use a Twitter account to send
notes to your office door, which is equipped with a 20x4 LCD
(display). This project presents basically an implementation of
the idea discussed in this IEEE Spectrum article:
http://spectrum.ieee.org/geek-life/hands-on/send-a-tweet-to-your-office-door
As an enhancement, it also displays the current temperature (read in
from weather.gov) in a given location. The implementation is based on
the combination of an Arduino Uno board with an Arduino Ethernet
Shield. The total cost is roughly $120. It is assumed that you
are familiar with Arduino tools.

Hardware and software
=====================

Hardware
--------
$45.95 - Arduino Ethernet Shield: http://www.sparkfun.com/products/9026
$29.95 - Arduino Uno: http://www.sparkfun.com/products/9950
$14.95 - 20x4 LCD White on Blue: http://www.hacktronics.com/LCDs/20-x-4-LCD-White-on-Blue/flypage.tpl.html (could get similar from sparkfun.com to minimize shipping costs)
$11.95 - Arduino Project Enclosure: http://www.sparkfun.com/products/10088 (hosts both the board and the shield)
$3.95 - USB Cable A to B - 6 Foot: http://www.sparkfun.com/products/512 (optional; you should have one already)
$2.99 - 9 Conductor 24 AWG Stranded-Shielded 6FT: http://www.frys.com/product/3550794?site=sr:SEARCH:MAIN_RSLT_PG (to hook-up the LCD display to the board)
$2.99 - 3FT CAT6 Patch Cable: http://www.frys.com/product/5979924?site=sr:SEARCH:MAIN_RSLT_PG (to hook-up the board to your Internet router)
$1.90 - 6 Pin Right Angle Female Header: http://www.sparkfun.com/products/9429 (two of them; for LCD connection)
$1.90 - Project Box: http://www.futurlec.com/ProjectBoxes.shtml (for display)
--------------
$116.53 - TOTAL

Software
--------
-- Arduino sketch: included at the end of this instructable. The sketch
must be changed to meet your individual specifications. It contains
comments with description of each change and how it may be done.
-- You will need to compile and load the sketch to the board with Arduino tools: http://arduino.cc/en/Main/Software
In order to be able to compile it you will also need the following libraries for your Arduino tools:
-- Arduino EthernetDHCP: http://gkaindl.com/software/arduino-ethernet/dhcp
-- Text Finder library: http://www.arduino.cc/playground/Code/TextFinder

Arduino sketch: bzdoor2lcd.pde
------------------------------

// Get "selected" latest tweet from Twitter account "BZDoor" and display it on the
// LCD display, which I have fixed on my office door. By selected I mean tweets that
// start with the key "BZD" only. I use the Twitter account to post notes on my
// office door about office hours, being out for coffee, etc. :-)
//
// The circuit:
// LCD pins 1,3,5,16 to Arduino digital pin GND
// LCD pin 2 to Arduino digital pin +5V
// LCD pin 15 via Resitor 330 Ohms to Arduino digital pin +5V
// LCD pin 14 to Arduino digital pin 2
// LCD pin 13 to Arduino digital pin 3
// LCD pin 12 to Arduino digital pin 4
// LCD pin 11 to Arduino digital pin 5
// LCD pin 4 to Arduino digital pin 9
// LCD pin 6 to Arduino digital pin 8


#include <SPI.h>
#include <Ethernet.h>
#include <EthernetDHCP.h>
#include <TextFinder.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(9, 8, 5, 4, 3, 2);

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x4F, 0x8A };
byte ip[] = { 192,168,1,177 };
byte server[] = { 199,59,148,10 }; // twitter.com
byte server_weather[] = { 140,90,113,200 }; // weather.gov

#define NUM_CHAR_USERNAME 7 // "BZDoor:" is username with 6 characters + :
#define TWEET_OFFSET NUM_CHAR_USERNAME + 5 // useful info from tweet to be displayed appears after "BZDoor: BZD "
char tweet[NUM_CHAR_USERNAME + 140]; // 147 = "BZDoor:" + 140 for actual tweet
char old_tweet[] = { "BZDoor: BZD No updates. Have a nice day! Current temp: NA Cris " };
char temperature[8];

Client client(server, 80);
TextFinder finder(client);
Client client_weather(server_weather, 80);
TextFinder finder_weather(client_weather);


void setup()
{
lcd.begin(4,20);
lcd.clear();
lcd.print("BZDoor2LCD");
Serial.begin(9600);
Serial.println("BZDoor2LCD");
Ethernet.begin(mac, ip);
}

void loop()
{
// (1) check if new tweet is present in BZDoor account;
check_for_new_valid_tweet();
// (2) check for temperature; also display signature;
check_for_new_temperature();
// (3) clean up;
client.stop();
client.flush();
client_weather.stop();
client_weather.flush();
// (4) also clear the "tweet" storage array; otherwise if latest tweet is shorter
// than the previous one, remnants from the previous will become part of the latest;
for ( int i=0; i<NUM_CHAR_USERNAME+140; i++) tweet[i] = ' ';
// (5) delay 2 minutes before next update;
delay(120000);
}


void check_for_new_valid_tweet() {
// responsible with 1st and 2nd rows of the 4x20 LCD;
// connect to Twitter (RSS of user BZDoor: 316663659) and get the latest tweet;
// this check will be done every other 2 minutes, so if more tweets have been posted
// have been posted within these 2 minutes only the latest tweet will be processed;
if ( client.connect()) {
client.println("GET http://www.twitter.com/statuses/user_timeline/316663659.rss HTTP/1.0"); // BZDoor;
client.println();
} else {
lcd.setCursor(0,0); lcd.print( "Twitter connection ");
lcd.setCursor(0,1); lcd.print( "failed! ");
Serial.println("Twitter connection failed.");
}
if ( client.connected()) {
// get the last tweet in BZDoor's account by simply parsing the item and title tags;
if ( (finder.find("<item>") && (finder.getString("<title>","</title>",tweet,140)!=0)) ) {
// only tweets that start with the three characters "BZD" will be displayed
// on the LCD display on the office door; all others will be printed only to
// the Arduino's serial monitor
Serial.println(tweet);
// a regular tweet in BZDoor's account is always in one of two formats:
// 1) "BZDoor: This is a Hello World! tweet!", which is not displayed on LCD;
// 2) "BZDoor: BZD Some update", which is displayed on LCD because starts with key BZD;
if ( tweet[8] == 'B' && tweet[9] == 'Z' && tweet[10] == 'D') {
// this is a tweet that has an update from BZDoor to be displayed on the office door;
display_tweet(tweet);
// also copy this latest twit to old_tweet array;
strncpy(old_tweet, tweet, 80+TWEET_OFFSET);
} else {
display_tweet(old_tweet); // first time this says no new updates;
}
} else {
lcd.setCursor(0,0); lcd.print( "Could not find item ");
lcd.setCursor(0,1); lcd.print( "field in last twit ");
Serial.println("Could not find item field in last twit");
}
} else {
lcd.setCursor(0,0); lcd.print( "Disconnected from ");
lcd.setCursor(0,1); lcd.print( "twitter ");
Serial.println("Disconnected from twitter");
}
}

void display_tweet( char msg[]) {
// display only 40 characters on the first two rows of the
// 20x4 LCD display; the third and fourth rows are used for
// temperature and signature display;
lcd.clear();
// display tweet's first 40 chars;
lcd.setCursor(0,0);
for ( int i=TWEET_OFFSET; i<TWEET_OFFSET+20; i++) {
if (msg[i] != 0) lcd.print(msg[i]);
else lcd.print(' ');
}
lcd.setCursor(0,1);
for ( int i=TWEET_OFFSET+20; i<TWEET_OFFSET+40; i++) {
if (msg[i] != 0) lcd.print(msg[i]);
else lcd.print(' ');
}
// print also to serial monitor;
char buffer_row[20];
strncpy( buffer_row, msg+TWEET_OFFSET, 20); buffer_row[20] = '\0';
Serial.println(buffer_row);
strncpy( buffer_row, msg+TWEET_OFFSET+20, 20); buffer_row[20] = '\0';
Serial.println(buffer_row);
}


void check_for_new_temperature() {
// responsible with 3rd and 4th rows of the 4x20 LCD;
// connect to www.weather.gov and get temperature for Fargo airport;
if ( client_weather.connect()) {
client_weather.println("GET /xml/current_obs/KFAR.xml HTTP/1.0");
client_weather.println();
} else {
lcd.setCursor(0,2); lcd.print( "Weather connection ");
lcd.setCursor(0,3); lcd.print( "failed! ");
Serial.println("Weather connection failed.");
}
if ( client_weather.connected()) {
if ( (finder_weather.getString("<temp_f>","</temp_f>",temperature,8)!=0) ) {
lcd.setCursor(0,2);
lcd.print("Curr temp: "); lcd.print(temperature); lcd.print(" F");
lcd.setCursor(0,3); lcd.print("Cris ");
// print also to serial monitor
Serial.println("Curr temp: "); Serial.println(temperature); Serial.println(" F");
Serial.println("Cris ");
} else {
lcd.setCursor(0,2); lcd.print("Current temp: NA");
lcd.setCursor(0,3); lcd.print("Cris ");
Serial.println("Couldn't find temp_f in weather stream");
}
} else {
lcd.setCursor(0,2); lcd.print("Current temp: NA");
lcd.setCursor(0,3); lcd.print("Cris ");
Serial.println("Disconnected from weather");
}
}