Simple SMS Controlled Stuff HOWTO

Date: 1273120004
Feel free to contact me/share cool ideas/etc. - name: bbklopfer domain: gmail dot (duh).

Introduction

Until mid 2009, I didn't have a cell phone. I think they're kinda silly (I'm nowhere near important enough for other people to need to be in contact with me 24/7). In any event, when life gives you lemons, try to do something that Calvin and Hobbes might have done; go to great lengths to be lazy!

Requirements

  • Computer with internet access and a relay/TRIAC box (see Coffee Making)
  • Google Voice account
  • Cell phone capable of sending plain ol' texts (no data plan needed, though an unlimited text plan is strongly recommended)
  • Shell + email account on a *NIX box (with a FQDN or at least a static IP).
  • Lights/stereo/guitar amp, etc.
  • People to annoy

  • For the sake of simplicity, I'll assume that the computer runs *NIX and interfaces with the relay/TRIAC box via a parallel port (I used an old DEC laptop with Slackware).

    Overview of Operation (if you can call it that)

  • Text message gets sent from phone to Google Voice
  • Google Voice forwards to email host
  • Local box tails mail file (e.g., /var/mail/$USER) via ssh, and pipes it to some useful program
  • Useful program switches lights
  • omg ur lites jst trnd on!1

    For generality's sake, I'll assume that the email host and local box are different. If they're not, you can just eliminate all the ssh'ing involved in the more general case. Now, assuming our useful program is called smsh, we have the following on our local SMS box:
    $ ssh $REMOTE_USER@$EMAIL_HOST tail -f /var/mail/$REMOTE_USER | smsh
    
    That's it. All that's required is a little email parsing by smsh. To actually control the switches, smsh just needs to invoke some intermediate program (obviously, this functionality can just be integrated into smsh if you prefer). For a parallel port setup, this can be achieved by some very simple code, the usage of which is self explanatory:
    /* simple_parcon.c */
    #include <stdio.h>
    #include <sys/io.h>
    
    #define IO_BASE 0x378
    
    int main(int argc, char **argv)
    {
      if (!(argc == 1 || argc == 3)) {
    		printf("Wrong num args\n");
    		return 1;
    	}
    
    	if (ioperm(IO_BASE, 1, 1)) {
    		printf("Error setting permissions to port.\n");
    		return 1;
    	}
    	
    	if (argc == 3) {
    		int mask = atoi(argv[1]);
    		int port = inb(IO_BASE);
    		if (*argv[2] == '0') /* Off */
    			outb(port - (mask & port), IO_BASE);
    		else if (*argv[2] == '1') /* On */
    			outb(mask | port, IO_BASE);
    		else if (*argv[2] == 'i') /* Invert */
    		  outb(mask ^ port, IO_BASE);
    	}
    	
    	printf("%d\n", inb(IO_BASE));	
    	
    	return 0;
    }
    
    Once you have the relay/TRIAC box built, the whole thing should take less than an hour to setup with a simple smsh program (a simple implementation can be hacked up with a shell script).

    Additional Functionality

    To send a reply text message (via email), we have to be sure to send it from the host which received the original email (this appears to be a Google Voice requirement -- not a bad idea). Since we're assuming ssh access to the email host, we can just run something like
    $ echo $BODY | ssh $REMOTE_USER@$EMAIL_HOST mail $EMAIL_ADDRESS
    
    Where $EMAIL_ADDRESS was just parsed from the text message. Obviously, passwordless ssh is useful for invoking this from a script.

    As far as other functionality goes, simple security isn't a bad idea. Since the caller's phone number is easily parsed from the email, phone number based authentication is trivial. The ability to authenticate the phone by texting the password, and multi-level privileges (e.g., turning off the beer fridge or replying with a 'fortune -o' might be off limits to all but the most trusted users) are handy features. At this point, you can be running arbitrary commands on your box. Might be a good idea to keep the user running smsh out of the sudoers file, though...

    Adding Physical Control

    Controlling lights from a cell phone is all fine and dandy, but it's nice to be able to control the lights from a keyboard as well. Assuming you already have something like simple_parcon compiled, an ugly bash one-liner works fine (note that this requires keeping a user logged in to the in-focus terminal):
    $ while true; do read -n 1 pin; if [ $pin -ge 0 ] && [ $pin -le 7 ]; then simple_parcon $((2**$pin)) i; fi; done
    
    An easy way of getting around the "user must be logged in" constraint on Linux is just to read input_events from the appropriate /dev/input/event* file (see linux/input.h -- sadly, I've no experience with *BSD).

    Further Improvements

    I'm now working on setting this up with a small ARM board, instead of my ancient laptop. Communicating with tail over ssh is being scrapped for sockets, among other improvements. Updates may or may not be posted here.

    Pictures

    Relay/TRIAC box + ARM board = SMS box:



    The DEC laptop...Pentium, made for Windows 95. Put Slack on it, and it's pretty useful!

    Musings

    Turning on/off lights (and waiting for replies) is an interesting way to measure network latency. Unfortunately, it's difficult to separate the cell latency from the email + google voice latency. That said, there are certainly trends; early in the morning and late at night are often quite responsive for me, whereas early evening can be very sluggish.

    Additionally, repeat texts get dropped now and then; texting "lamp on" followed by "lamp off" in a relatively short time period results in a non-trivial amount of dropped texts (again, not sure if google or my cell provider is doing this). However, modifying the text slightly (e.g., by changing case) seems to trick it into going through. I suppose this makes sense, but the duplicate detection gets a little annoying.

    Acknowledgments

    Thanks to my housemates for suggesting the idea, and to my girlfriend putting up with me as I paid more attention to my cell phone than to her (and even after all that, she bought me an O'Reilly book for my birthday!).