Showing posts with label twitter. Show all posts
Showing posts with label twitter. Show all posts

Tuesday, July 30, 2013

Twitter, again


Using the Arduino ethernet shield on the Mega 2560 Arduino, this displays tweets.  But not directly - Twitter's new API requires SSL, and the Arduino isn't up to doing that.  So I set up a CGI program on my desktop computer which checks a twitter feed, and returns a plain text version of the username and message.  Code for the CGI program:

#!/usr/bin/python

import twitter, sys, time, pickle

class Timeline:
    def __init__ (self):
        self.seen = set()
        self.cache = []

    def add_to_cache (self, statuses):
        for d in [s.AsDict() for s in statuses]:
            if d["id"] not in self.seen:
                self.seen.add(d["id"])
                self.cache.append(d)

    def __iter__ (self):
        return self

    def next (self):
        if len(self.cache) == 0:
            raise StopIteration
        else:
            return self.cache.pop()
 
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:
    try:
        timeline = pickle.load(open("timeline.dat"))
    except:
        timeline = Timeline()

    timeline.add_to_cache(api.GetUserTimeline("LE17RH"))

    print "Content-type: text/plain\n"
    try:
        x = timeline.next()
        print x["user"]["screen_name"]
        print x["text"]
    except StopIteration:
        pass

    pickle.dump(timeline, open("timeline.dat", "w"))

The Timeline class is an iterator because in an earlier incarnation I was loading a lot of tweets and wanting to iterate over them, it isn't necessary for this program which just returns a plain text rendition of one username and message.

Code for the Arduino:

#include <SPI.h>
#include <Ethernet.h>
#include <LiquidCrystal.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x7F, 0xB6 };
const IPAddress ip(143,210,109,74);
const IPAddress dnsserver(143,210,12,154);
const IPAddress server(143,210,108,92);
const char get_header[] = "GET /cgi-bin/twittino.cgi";
const char server_header[] = "Host: 143.210.108.92 80";

EthernetClient client;
LiquidCrystal lcd(8, 9, 17, 16, 15, 14);

const int SDSELECT = 4;
const int PIN_G    = 21; // Pins on Mega board!
const int PIN_R    = 20;
const int PIN_B    = 19;

void setup() {
  pinMode(PIN_R, OUTPUT);
  pinMode(PIN_G, OUTPUT);
  pinMode(PIN_B, OUTPUT);
  pinMode(SDSELECT, OUTPUT);
  digitalWrite(PIN_R, HIGH);
  digitalWrite(PIN_G, LOW);
  digitalWrite(PIN_B, HIGH);
  digitalWrite(SDSELECT, LOW);
  Serial.begin(9600);  
  lcd.begin(16, 2);
  Ethernet.begin(mac, ip, dnsserver);
  delay(1000);
  digitalWrite(SDSELECT, HIGH);
  Serial.println(Ethernet.localIP());
  digitalWrite(PIN_G, HIGH);
}

void loop()
{
  static int counter = 1;
  char username[30];
  char tweet[141];
  
  Serial.println(counter++);
  
  if (client.connect(server, 80)) 
  {
    // Serial.println("Connected");
    // Make a HTTP request:
    client.println(get_header);
    client.println(server_header);
    client.println("Connection: close");
    client.println();
    int ulen = 0;
    int tlen = 0;
    boolean username_found = false;
    while (client.connected())
    {  
      if (client.available()) 
      {
        char c = client.read();
        if (!username_found)
        {
          if (c == '\n')
          {
            username_found = true;
            username[ulen] = '\0';
          }
          else
          {
            if (c != '\n') username[ulen++] = c;
          }
        }
        else
        {
          if (c == '\n') 
          {
            tweet[tlen++] = ' ';
          }
          else
          {
            tweet[tlen++] = c;
          }
          switch(c)
          {
            case 'R' : digitalWrite(PIN_R, LOW); break;
            case 'G' : digitalWrite(PIN_G, LOW); break;
            case 'B' : digitalWrite(PIN_B, LOW); break;
            case 'r' : digitalWrite(PIN_R, HIGH); break;
            case 'g' : digitalWrite(PIN_G, HIGH); break;
            case 'b' : digitalWrite(PIN_B, HIGH); break;
          }
        }
      }
    }
    client.stop();
    // Serial.println("Disconnected");
    tweet[tlen] = '\0';
    if (ulen != 0 && tlen != 0)
    {
      Serial.print("Username : ");
      Serial.println(username);
      Serial.print("Tweet    :\n");
      Serial.println(tweet);
      lcd.setCursor(0, 0);
      for (int i=0; i < ulen; i++)
      {
        lcd.print(username[i]);
      }
      for (int i=0; i <= tlen; i++)
      {
        lcd.setCursor(0, 1);
        for (int j=0; j < 16; j++)
        {
          if (j + i < tlen) lcd.print(tweet[j+i]);
        }
        lcd.print(' ');
        delay(500);
      }
    }
    else
    {
      Serial.println("Nothing.");
    }
  } 
  else 
  {
    Serial.println("Connection failed.");
  }
  delay(10000); // Check every 10 seconds
  lcd.clear();
}

Things to note: the SD card, if present, needs to be deactivated (by setting SD_SELECT, pin 4, to LOW) before setting up the ethernet connection.  In addition to the LCD panel, I have one of my RGB LED units connected, and if there is a capital R, G or B in the tweet, the appropriate colour is displayed (the lowercase equivalents turn the colour off).  The username is displayed on the top row of the LCD panel, and the tweet scrolls along the bottom row.

 

Monday, July 1, 2013

Open Day, Part II

The main things I learned from the open day:
  1. It is possible to mull over these things for a few weeks when you are too busy to do anything serious, and then put in a few hours' work late on a Friday afternoon and come up with something working.
  2. iPads are fun, and there are some great apps to do serious engineering, but you can't hack those apps, and you can't do much to hack the iPad.  You get to do what Apple, and Apple-approved developers, allow you to do.  Which is a great deal, but it's not the sort of activity that engineers find really satisfying (and despite the fact that I'm an ex-geochemist with a master's degree in philosophy, I still consider myself a kind of engineer).
  3. On Friday afternoon, I needed the python-twitter library to check the twitter feed from Python.  Downloading and installing it via apt-get from the Ubuntu repository took a few seconds. When I realised I needed a newer version to cope with changes in Twitter's API, it took a couple of minutes to find the newer version, download it and install it.  If I had been using the locked-down campus version of Windows, it would not have been that trivial. Perhaps not even possible. Linux makes it much, much easier to do this sort of thing.
  4. I enjoyed myself more late on Friday afternoon than I had done for weeks doing more "important" things.  It really fired my enthusiasm for using these things in next year's programmable electronics module (I have sixty kits sitting in a cupboard), and encouraging students to play around with them and see what they can do.
  5. I thought I had fried the GPIO port on my Raspberry Pi.  A morning sitting in a room with disappointingly few visitors meant I had time to do some troubleshooting with a multimeter, and it turned out to be a broken connection on the ground rail of one of my breadboards, which was a relief.  I love my multimeter.
  6. To some visitors, sending a tweet to turn on an LED was like magic.  To some, it was deeply unimpressive.  Only colleagues were able to grasp how much was really going on between pressing the send button on the iPad, and the red and green lights coming on.

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.

Wednesday, July 25, 2012

Twitter Updating

Revised version of the code I originally wrote about here. The main difference is that I've included a counter so it doesn't post more than two tweets every time it is run (and since it only runs once an hour, this shouldn't make it too antisocial even when it hasn't run for a while and there are a lot of items in the RSS feed).
#!/usr/bin/python

import tweepy
import feedparser
import urllib
import urllib2

url = "http://www2.le.ac.uk/departments/engineering/news-and-events/blog/RSS"

oauth_file = open("access_token.txt", "r")
oauth_token = oauth_file.readline().rstrip()
oauth_token_secret = oauth_file.readline().rstrip()

consumer_file = open("consumer_keys.txt", "r")
consumer_key = consumer_file.readline().rstrip()
consumer_secret = consumer_file.readline().rstrip()

bitly_file = open("bitly.txt", "r")
bitly_username = bitly_file.readline().rstrip()
bitly_apikey = bitly_file.readline().rstrip()

bitly_base = "http://api.bit.ly/v3/shorten?"
bitly_data = {
    "login" : bitly_username,
    "apiKey" : bitly_apikey,
    "format" : "txt",
    "longUrl" : ""
    }

already_done = []
done_file = open("done.txt", "r")
for line in done_file:
    already_done.append(line.rstrip())
done_file.close()

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(oauth_token, oauth_token_secret)

api = tweepy.API(auth)
feed = feedparser.parse(url)

count = 0
for item in feed["items"]:
    url   = item["link"]
    title = item["title"]
    if url not in already_done and count < 2:
        bitly_data["longUrl"] = url
        to_shorten = bitly_base + urllib.urlencode(bitly_data)
        result = urllib2.urlopen(to_shorten).read()
        api.update_status(title + " : " + result)
        already_done.append(url)
        count = count + 1

done_file = open("done.txt", "w")
for url in already_done:
    done_file.write(url + "\n")
done_file.close()

Monday, December 20, 2010

An aside: Twitter and RSS

The Department of Engineering twitter news feed has been dead since the autumn, when Twitter ended simple authentication in favour of OAuth. I've just resurrected it (the sort of job I get round to doing in the week before Christmas), and here's how (for the OAuth part I found this blog post very helpful - there are various broken and/or poorly-documented attempts to link Python, OAuth and Twitter around the web).
  1. Sign in to Twitter, and go to http://dev.twitter.com/ - then "Your apps" (top of the page) and register a new application.
  2. Assuming you've registered correctly, the next page will include a consumer key (a long alphanumeric string) and a consumer secret (an even longer alphanumeric string). Put these in a text file called consumer_keys.txt.
  3. Go to "My Access Token" (right hand side of the page) to get your access token. Two long strings again, put these in a text file called access_token.txt.
  4. Go to http://bit.ly, sign in, and then http://bit.ly/a/your_api_key
    - put your bit.ly username and this API key in a text file called bitly.txt
  5. Install tweepy and feedparser. (Some useful info on feedparser here).
  6. Code is below (note that I have shortened the RSS URL to prevent it running off the screen on this blog entry - in the actual program it needs to be the full RSS URL). I now just need to add a cron job to my PC, and it will check the website's news RSS feed and update Twitter periodically. You could, of course, simply put the various API and OAuth keys directly into the code, instead of having them in their own files.

#!/usr/bin/python

import tweepy
import feedparser
import urllib
import urllib2

rss_url = "http://www2.le.ac.uk/.../RSS"

oauth_file = open("access_token.txt", "r")
oauth_token = oauth_file.readline().rstrip()
oauth_token_secret = oauth_file.readline().rstrip()

consumer_file = open("consumer_keys.txt", "r")
consumer_key = consumer_file.readline().rstrip()
consumer_secret = consumer_file.readline().rstrip()

bitly_file = open("bitly.txt", "r")
bitly_username = bitly_file.readline().rstrip()
bitly_apikey = bitly_file.readline().rstrip()

bitly_base = "http://api.bit.ly/v3/shorten?"
bitly_data = {
"login" : bitly_username,
"apiKey" : bitly_apikey,
"format" : "txt",
"longUrl" : ""
}

already_done = []
done_file = open("done.txt", "r")
for line in done_file:
already_done.append(line.rstrip())
done_file.close()

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(oauth_token, oauth_token_secret)

api = tweepy.API(auth)
feed = feedparser.parse(rss_url)

for item in feed["items"]:
url = item["link"]
title = item["title"]
if url not in already_done:
bitly_data["longUrl"] = url
to_shorten = bitly_base + urllib.urlencode(bitly_data)
result = urllib2.urlopen(to_shorten).read()
api.update_status(title + " : " + result)
already_done.append(url)

done_file = open("done.txt", "w")
for url in already_done:
done_file.write(url + "\n")
done_file.close()