Sunday, February 22, 2009

GMail Notifier on an Arduino

The exams are over and I've been hacking a bit on the Arduino today. So I came up with a simple hack which blinks an LED on the Arduino if you've got unread mails in your Gmail inbox. I assume that you're familiar with the basics of Arduino.

Equipment

  • A computer with an internet connection

  • Python

  • pySerial

  • Arduino ( I'm using Duemilanove )

  • Red LED

  • Pushbutton (optional)

  • Wires

On the computer

A python script will run continuously on the computer, and fetch the Gmail RSS feed every few minutes. pySerial will be used to notify the Arduino of new mails.

Here are our imports and constants

# ~ Gmail Notifier for Arduino
# ~ This file is released under the public domain

import httplib
import getpass
import base64
import re
import time
import serial

INTERVAL = 5 # check every INTERVAL minutes

serv = 'mail.google.com'
path = '/mail/feed/atom'
# ask user name and password and encode them for authentication

auth = base64.encodestring(
'%s:%s'%(raw_input('Username: '),
getpass.getpass()))


So first, fetching the feed. We'll use httplib. Here is the code:

def getfeed():
print 'Checking...'
conn = httplib.HTTPSConnection(serv)
conn.putrequest('GET', path)
conn.putheader('Authorization', 'Basic %s'%auth)
conn.endheaders()
return conn.getresponse().read()


Next lets get the count. Gmail replies in the following format. In the case of new mails there is more information, but we don't care about that. We're mainly interested in fullcount.

<?xml version="1.0" encoding="UTF-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<title>Gmail - Inbox for nsm.nikhil@gmail.com</title>
<tagline>New messages in your Gmail Inbox</tagline>
<fullcount>0</fullcount>
<link rel="alternate" href="http://mail.google.com/mail" type="text/html" />
<modified>2009-02-22T11:00:33Z</modified>
</feed>


So we'll use regular expressions to get the count.

def count(data):
matches = re.findall('<fullcount>([0-9]+)</fullcount>', data)
if len(matches) == 0:
print 'Error in parsing feed, check user name and password are correct'
return 0
return int(matches[0])


We'll need to write this to the serial port.

def writeSer(data):
try:
# the best way to find this out is to launch the Arduino environment
# and see what it says under Tools -> Serial Port
ser = serial.Serial('/dev/ttyUSB0')
ser.write(data)
except serial.serialutil.SerialException:
print 'Error writing to serial device'
raise


Now that we're done with the functions, it's time to make them work together


# subtract so that we check first time
last_check = time.time() - INTERVAL*60

while True:
if time.time() - last_check < INTERVAL*60:
continue
last_check = time.time()
msgs = count(getfeed())
print msgs,'mails'
writeSer(str(msgs))



Thats the computer part.

On the Arduino


The circuit :



The LED is on pin 13 and goes to ground. The button takes 5V through the power pins on the analog side, via a 220 ohm resistor. The other leg is grounded. Pin 4 can be used to read the state of the button. The wire from pin 4 connects to the 5V leg of the button.

The code is dead simple and so is presented together.


int ledPin = 13; // connect led to digital pin 13, or use default small one
int bPin = 4; // connect button to digital pin 4
boolean blink = false; // holds our current state

int INTERVAL = 200; // led blink rate in milliseconds

void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}

void loop() {
if(Serial.available() > 0)
blink = Serial.read() > 48;// 48 is 0 is ASCII

if(digitalRead(bPin) == LOW) // button pressed
blink = false;

// blink is true if we got serial input
// or we had got serial input and button hasn't been pressed yet.
if(blink) {
digitalWrite(ledPin, HIGH);
delay(INTERVAL);
digitalWrite(ledPin, LOW);
delay(INTERVAL);
}
else
digitalWrite(ledPin, LOW);
}


The button is used to switch off the blinking once you've noticed that you've got mail and don't want it to keep blinking till the next check.

Running

Verify and Upload the code to the Arduino. You can try entering numbers in the Serial Monitor to check that the circuit works. Remember to switch off the Serial Monitor.

Now start your script, python mailarduino.py. Enter authentication details. Now sit back and relax... oh wait, you've got mail.

Full python script ( mailarduino.py )

import httplib
import getpass
import base64
import re
import time
import serial

INTERVAL = 5 # check every INTERVAL minutes

serv = 'mail.google.com'
path = '/mail/feed/atom'

auth = base64.encodestring(
'%s:%s'%(raw_input('Username: '),
getpass.getpass()))

def count(data):
matches = re.findall('<fullcount>([0-9]+)</fullcount>', data)
if len(matches) == 0:
print 'Error in parsing feed, check user name and password are correct'
return 0
return int(matches[0])

def getfeed():
print 'Checking...'
conn = httplib.HTTPSConnection(serv)
conn.putrequest('GET', path)
conn.putheader('Authorization', 'Basic %s'%auth)
conn.endheaders()
return conn.getresponse().read()

def writeSer(data):
try:
ser = serial.Serial('/dev/ttyUSB0')
ser.write(data)
except serial.serialutil.SerialException:
print 'Error writing to serial device'
raise

last_check = time.time() - INTERVAL*60 # subtract so that we check first time

while True:
if time.time() - last_check < INTERVAL*60:
continue
last_check = time.time()
msgs = count(getfeed())
print msgs,'mails'
writeSer(str(msgs))