Creating a Presence Twitter-bot

Here's a quick and easy project to create a presence tracking Twitter-bot.
This project came up as a challenge by a member at Protospace, my local hackerspace.

Goal: Create an optional, anonymous tool to tweet when the space is opened and closed.

I was a little late engaging in this project and left myself about 36hrs from decision to demo. That timeline impacted my design - I decided to go with off-the-shelf parts.

Parts List:
Arduino Uno
Arduino Uno Wireless Shield
Roving Networks RN-XV WiFi Module
Switch
Enclosure
9v regulated DC adapter

Assembly:
The Enclosure was designed to host the Uno so fit is not a problem. What is a minor problem is that the front edge of the WiFly sticks out past the edge of the shield. Dremeling about 1/16" off the front of the WiFly for a snug fit within the Enclosure.

Assemble the bottom and sides of the Enclosure. Now mount the Uno to the bottom plate. Insert the RN-XV WiFi (WiFly) into the shield and then mount the shield into the Uno.

Prepare the switch by soldering two short lengths of wire to the terminals. 3" each is probably about right, strip one end 1/8th and the other 1/4". The shorter stripped end goes to the switch and the longer will be inserted into the shield header.

Drill a hole in the top plate of the enclosure. My enclosure was made of acrylic so it was no trouble to drill. Install the switch into the top plate. The GND and Pin 7 are almost directly across the shield from each other. Connect one wire to GND on the shield (there are two, it doesn't matter which one you use). Connect the other wire to DIO 7.

Environment:
I am using the Arduino 1.0 development environment.
My WiFly is at firmware version 2.31.
My Wireless Access Point is configured "Open" with an SSID of MySSID, there is no WEP or WPA security.
You need an OAuth token ... details here

Configuring the WiFly:
There are two approaches that we could follow:

  1. Initialize the WiFly from factory everytime we boot
  2. Load a saved configuration everytime we boot

I went with #2 - it was easier to implement and required less code. One downside is that your software now has to assume the configuration of the device. Configuration is easier though as it does not require code changes.

Initial WiFly configuration:
Look on the Wireless Proto Shield - there is a small switch that can select between USB and Micro. Select USB.
Connect your computer to the Uno's USB port. Lights on the Uno, Shield & WiFly should flash, there shouldn't be any smoke:)
When looking at the WiFly the Red LED should be blinking steadily with the Green LED blinking at 1/2 the rate of the RED. If you watch long enough you'll see the WiFly pause with only the green LED on - not sure what it's doing - maybe looking for the default SSID.

Create an empty Arduino sketch and upload it to the Uno.

void setup() {}
void loop() {}

Open the Arduino serial monitor.
Set the line ending to "No line ending"
Enter 3 dollar signs: $$$
Click [Send]
You should get a CMD prompt.
Change the line ending to "Carriage return"
Click [Send]
You should get a <2.31> prompt
Enter the following commands:

set wlan ssid MySSID
set wlan join 1
set ip dhcp 3
join

You should get a couple of messages that show the WiFly connecting to your wireless network.
The WiFly should now only have the Green LED flashing.

set time enable 1
set ip proto 18
set dns name arduino-tweet.appspot.com
set ip host 0
set ip remote 80
set uart mode 2
set com remote 0
set comm size 1024

Now we save and reboot the WiFly:

save
reboot

You will see a couple of messages scroll by and if everything is working you'll get something a lot like this:

*Reboot*WiFly Ver 2.31, 01-01-2012 on RN-171
MAC Addr=00:06:66:aa:bb:cc
Auto-Assoc MySSID chan=6 mode=OPEN SCAN OK
Joining MySSID now..
*READY*
Associated!
Using DHCP Cache
Listen on 2000

Loading the software:
Copy and paste the attached sketch as a starting point. You'll need to substitute your own OAuth string and message(s).

// Look here for outline:
// https://arduino-tweet.appspot.com/
#include 

#define GUARD_TIME 250
#define SWITCH_PIN 7
#define SET_COMM   "$"
#define TWITTER_HOST  "arduino-tweet.appspot.com"
#define SSID          "MySSID"
#define TWITTER_TOKEN "Put your OAuth Token Here"


// I am outputting debug messages to a software serial port on pins 2 & 3.
// These debugging messages are entirely optional and may be removed if desired.
SoftwareSerial mySerial(2, 3);


// Message to post
char open_msg[]        = "Someone is here";
char close_msg[]       = "But now they left";

int togglePin(int whichPin)
{
  static int state = LOW;

  if(state == LOW) {
     state = HIGH; }
  else {
     state = LOW; }

  digitalWrite(whichPin, state);
  return state;
}

int drain_serial_input()
{
  int char_count = 0;
  long timeout;
  // This drains the serial input queue.
  // The timeout is 0.5sec after the last received character.
  // Copy anything received to LCD.  
  timeout = millis() + 500;
  while(millis() < timeout) {
    if(Serial.available() > 0) {
      char_count++;
      timeout = millis() + 500;
      mySerial.write(Serial.read());
      }
    }
  return char_count;
}


void setup()
{
  Serial.begin(9600);
  pinMode(SWITCH_PIN, INPUT);
  digitalWrite(SWITCH_PIN, HIGH); // Turn on internal resistor.
  pinMode(13, OUTPUT);

  mySerial.write(0x0C);
  delay(5);
  mySerial.print("\rDrained ");
  mySerial.print( drain_serial_input() );
  mySerial.print(" chars");
  delay(2000);
}

void loop()
  {
  char *msg_ptr = 0x00;
  char *uniquebuffer = "Unique Time";
  unsigned long int timeout;
  int old_switch_state, new_switch_state;
  old_switch_state = new_switch_state = digitalRead(SWITCH_PIN);

  while(true) {
    new_switch_state = digitalRead(SWITCH_PIN);
    if( new_switch_state != old_switch_state) {
       old_switch_state = new_switch_state;
       if(new_switch_state == HIGH) {
          msg_ptr = open_msg; }
       else {
          msg_ptr = close_msg; }
      }

    if( msg_ptr ) {
      sprintf(uniquebuffer, "%9ld", millis());
      
      Serial.println("POST http://" TWITTER_HOST "/update HTTP/1.1");
      Serial.print("Content-Length: ");
      Serial.println(strlen(msg_ptr)+strlen(TWITTER_TOKEN)+strlen(uniquebuffer)+16);
      Serial.println();
      Serial.print("token=" TWITTER_TOKEN);
      Serial.print("&status=");
      Serial.print(uniquebuffer);
      Serial.print(": ");
      Serial.println(msg_ptr);
      
      msg_ptr = 0x00;

      // Wait for a response from the web server
      mySerial.write(0x0C);
      delay(5);
      mySerial.print("Wait for Response");
      timeout = millis() + 1000;
      while((! Serial.available()) && (millis() < timeout)) {
        delay(5); }
       
      // Copy anything received to LCD.  This drains the serial input queue.
      mySerial.write(0x0C);
      delay(5);
      mySerial.print("Drain Response");
      timeout = millis() + 1000;
      while(millis() < timeout) {
        if(Serial.available() > 0) {
          timeout = millis() + 1000;
          mySerial.write(Serial.read());
          }
        }
      }

    delay(100);
    togglePin( 13 );
    }
  }

Upload your sketch to the Uno and then flip the switch on the Arduino Wireless Shield from USB to Micro.

That's it ... you should be off to the races.

Resources:
All parts purchased from Solarbotics.
Roving Networks RN-XV data available at their web-site

Modifications:
Try using a photo transistor if you want to make the device control completely passive.

Extensions:
Validate the return HTTP stream to ensure that your attempted tweet succeeded.