Monday, July 1, 2013

Open Day, Part I

I was asked to demonstrate the use of an iPhone in Engineering at Saturday's alumni open day.  I demonstrated AutoDesk ForceEffect Motion (which is pretty good fun) on the iPad rather than iPhone, but also demonstrated using the iPad to send tweets to the department's Twitter account (@le_engineering), which was more fun for me to do.  I had a Python program running on my Linux netbook, which checked Twitter for mentions of @le_engineering, and then searched the rest of the text for the characters R, G, r, g, b and 1 or 0.  These characters were sent via USB to an Arduino, which interpreted them as commands to light (uppercase) or turn off (lowercase) a red and green LED, sound a buzzer, or move a servo to 90 or 0 degrees.


Code for Python on Linux.  

I used the OpenAuth keys generated here.  I hadn't appreciated how great pickle is until now.  Because of Twitter's limits on API calls, and the fact that I wasn't expecting vast numbers of tweets (in the event, I was the only person who used this), I got the program to check the feed once every minute, and to stop after ten minutes.  Needs to be run under sudo, or you need to add your own account to the dialout group (sudo adduser $USER dialout) to be able to write to the serial port.  Obviously, you need to change the port device (currently /dev/ttyACM0) if that isn't the serial port your Arduino is attached to.

#!/usr/bin/python

import twitter, serial, sys, time, pickle

try:
    ser = serial.Serial(port="/dev/ttyACM0", baudrate=9600, timeout=0)
    # Allow the port to wake up, if using this in non-interactive mode
    time.sleep(0.5)
except:
    print "Failed to open port."

# Load the times of tweets that have already been dealt with (into a set). 
times_seen = pickle.load(open("times-seen.dat"))

oauth_file = open("access_token.txt", "r")
akey = oauth_file.readline().rstrip()
asec = oauth_file.readline().rstrip()

consumer_file = open("consumer_keys.txt", "r")
ckey = consumer_file.readline().rstrip()
csec = consumer_file.readline().rstrip()

try:
    api = twitter.Api(consumer_key=ckey, consumer_secret=csec,
        access_token_key=akey, access_token_secret=asec)
except Exception as e:
    print e
else:
    for i in range(10):
        # Check the twitter feed every minute for ten minutes
        # (API limit is 100 calls per hour)
        print "Minute {0}/10".format(i)
        try:
            mentions = api.GetMentions()
            for m in mentions:
                t = m.GetCreatedAt()
                if t in times_seen: continue
                times_seen.add(t)
                print "{0}: {1}".format(m.GetUser().name, m.text)
                text = m.text.replace("@le_engineering", "")
                for c in text:
                    try:
                        if c in "rgRG10b":
                            print c,
                            ser.write(c)
                            time.sleep(1)
                    except:
                        pass
                print
        except twitter.TwitterError as te:
            print te.message
        time.sleep(60)

# Save the time of tweets already dealt with.
pickle.dump(times_seen, open("times-seen.dat", "w"))
print "Done."

Arduino code.

Not much to say about this.  The playTone function is ripped off from Oomlout, but all this program really does is check for incoming characters on the serial port, and perform an action if a valid character is found.

#include <servo.h>

Servo myservo;

int green_led    = 7;
int red_led      = 6;
int speaker      = 5;
int servo_pin    = 3;

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speaker, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speaker, LOW);
    delayMicroseconds(tone);
  }
}

void setup() {               
  pinMode(green_led, OUTPUT);     
  pinMode(red_led, OUTPUT);   
  pinMode(speaker, OUTPUT);  
  myservo.attach(servo_pin);
  myservo.write(0);
  playTone(1000, 500);
  Serial.begin(9600);
}

void loop() {
  byte command;
  int i;
  
  if (Serial.available())
  {
     command = Serial.read();
     Serial.write(command);
     switch (command)
     {
       case 'G': digitalWrite(green_led, HIGH); break;
       case 'R': digitalWrite(red_led, HIGH); break;
       case 'g': digitalWrite(green_led, LOW); break;
       case 'r': digitalWrite(red_led, LOW); break;
       case 'b': playTone(2000, 500); break;
       case '0': myservo.write(0); break;
       case '1': myservo.write(90); break;
       default: break;
     }
  }
}

Hardware 


Thanks to Fritzing,which I found thanks to John Boxall's book Arduino Workshop.  The capacitor linked to the reset pin is in there because in an earlier iteration of this, I was attaching it to a web server rather than sending it instructions from a Python program.  Without the capacitor, the system reset every time a new serial connection was made, which wasn't the behaviour I wanted.  This explains why, and how to fix it.

1 comment:

  1. just to let you know where it says #include the s in servo has to be a capital

    ReplyDelete