Massachusetts Institute of Technology

Media Lab

Matt Hirsch

Twitter Laundry

Introduction

This page describes how we turned some electronic junk we found in a spare parts bin into a twittering waching machine and dryer.

Ingredients

PICDEM.net PIC Ethernet demo board by iosoft.

picdem.net demo board

Marvell plug computer

sheevaplug

Twitter.com

twitter.com

Relays from RadioShack

Topology

Since we don't have source code for the PICDEM.net board, we were forced to use it as-is.

Therefore, the topology of our twitter system looks like this:

topology

The sheevaplug runs a script which periodically contacts the PICDEM.net board. The PICDEM.net board, by default, comes with a web page that reports on the state of the analog input pins AN0 and AN1:

chipweb homepage

The washer and dry are connected to the analog pins of the PICDEM.net board using one relay each. We located a point in the wiring for both the washer and dryer that become 120V when the machines are activated. The relay, which has a large internal impedance, is wired between this point and ground. When the machine turns on, the relay trips, setting the cooresponding analog pin of the PICDEM.net board from 0V to 5V.

My landlord and friend Adam Wilson wired the relays in to the washer and dryer. You can see the steps here:

unmodified washer and dryer
The unmodified washer and dryer.

fuse box
Safety first! Turn off the fuse to the washer and dryer before working on them.

opening the dryer
Opening the instrument panel of the dryer. The green paper is the wiring diagram, which we used to determine where to put the relay.

dryer wiring diagram
The dryer wiring diagram. We'll put the relay at the tip of this screwdriver.

relay
This is the 120V relay (from RadioShack). The wires were first soldered to the contacts of the relay, and then sealed with hot glue for safety.

splicing in the relay
The relay is spliced in to the correct location in the dryer circuit. This is an image of the back side of the instrument panel.

splicing complete
The relay is now sliced in to the circuit. The gray wire goes out to the PICDEM.net board.

updated wiring diagram
Adam has updated the dryer's wiring diagram in case any hapless repairmen come along to fix our dryer.

opening up the washer instrument panel
Opening up the washer instrument panel.

washer wiring diagram
The wiring diagram for the washer.

splicing the relay into the washer
Here, Adam is splicing the relay in to the correct location in the washer's control circuitry, also determined by checking the included wiring diagram. It's great that these appliances are open source!

updated washer wiring diagram
The updated washer wiring diagram.

completed job!
This image shows the relays successfully spliced into the washer and dryer control circuits. They're now ready to be hooked in to the PICDEM.net board (shown above).

Code

I've updated the code for the tweeting laundry to be a single python script, which supports oauth authentication to twitter. The code is shown below. It is run in a screen session. You would have to replace the hashes with your own oauth secrets.

The project uses python-twitter. To get your keys set up, check out the example in python-oauth2 under Twitter Three-legged OAuth Example

#!/usr/bin/python

import urllib
import HTMLParser
import sys
import twitter
import time

board_address = '192.168.1.16'
ck = '############'
cs = '############'

washer_access_token_key =    "#####################";
washer_access_token_secret = "#####################";
dryer_access_token_key =     "#####################";
dryer_access_token_secret =  "#####################";

past_washer_value = -1
past_dryer_value = -1
voltage_threshold = 3.0

sleeptime = 5

logfile = "/home/mhirsch/washerdryertweet/decode_tweet.log"

class WasherHTMLParser(HTMLParser.HTMLParser):

    def __init__(self):
        HTMLParser.HTMLParser.__init__(self)
        self.reset_vals()

    def handle_starttag(self, tag, attrs):
        #print "starttag: ", tag, attrs
        #print len(attrs)
        if(tag == 'td' and len(attrs) == 2 and attrs[0] == ('colspan','2') and attrs[1] == ('align','center')):
            self.getval = True
    def handle_data(self, data):
        #print "data: ", data
        if(self.getval):
            #print 'getval True'
            self.vals.append(data)
            self.getval = False
    def reset_vals(self):
        self.vals = []
        self.getval = False
    def get_vals(self):
        return [float(self.vals[0]),float(self.vals[1])]

washerapi = twitter.Api(consumer_key = ck, consumer_secret = cs, access_token_key = washer_access_token_key, access_token_secret = washer_access_token_secret)
dryerapi = twitter.Api(consumer_key = ck, consumer_secret = cs, access_token_key = dryer_access_token_key, access_token_secret = dryer_access_token_secret)
parser = WasherHTMLParser()

try:
    while True:
        changed = False

        f = urllib.urlopen('http://' + board_address + '/stat1.egi')
        s = f.read()
        f.close()

        parser.feed(s)

        vals = parser.get_vals()
        if(len(vals) != 2):
            print >> sys.stderr, 'Did not get two values out: ', vals
        parser.reset_vals()

        try:
            timestring = time.strftime('%a %b %d %H:%M:%S %Z %Y')

            if(vals[0] > voltage_threshold and past_washer_value <= voltage_threshold):
                if(past_washer_value >= 0):
                    print "washer turned on"
                    message = timestring + ': I\'ve begun my darlings'
                    status = washerapi.PostUpdate(message)
                    print status.text

                changed = True
            elif(vals[0] <= voltage_threshold and past_washer_value > voltage_threshold):
                print "washer turned off"
                message = timestring + ': Our journey is at an end, my darlings.'
                status = washerapi.PostUpdate(message)
                print status.text
                changed = True
            elif(past_washer_value == -1):
                past_washer_value = vals[0]

            if(vals[1] > voltage_threshold and past_dryer_value <= voltage_threshold):
                if(past_dryer_value >= 0):
                    print "dryer turned on"
                    message = timestring + ': RARGH DRYER GO'
                    status = dryerapi.PostUpdate(message)
                    print status.text

                changed = True
            elif(vals[1] <= voltage_threshold and past_dryer_value > voltage_threshold):
                print "dryer turned off"
                message = timestring + ': DRYER END'
                status = dryerapi.PostUpdate(message)
                print status.text
                changed = True
            elif(past_dryer_value == -1):
                past_dryer_value = vals[1]

        except Exception, e:
            print 'Exception occured: ', e
            raise

        if(changed):
            print 'change detected: ', vals[0], past_washer_value, vals[1], past_dryer_value
            past_washer_value = vals[0]
            past_dryer_value = vals[1]
            if(vals[0] > voltage_threshold):
                d1 = '1'
            else:
                d1 = '0'
            if(vals[1] > voltage_threshold):
                d2 = '1'
            else:
                d2 = '0'

            ctrlurl = 'http://' + board_address + '/digout' + d1 + d2 + '.htm'
            f = urllib.urlopen(ctrlurl)
            f.read()
            f.close()

        time.sleep(5)

except KeyboardInterrupt:
    print 'Got keyboard interrupt: Quitting.'

Results

You can see the ongoing results on the twitter pages of our washer and dryer:

18MansfieldWash

18MansfieldDry