PoetBot!

 

For this project, I wanted to make a machine that took numerical input and produced something creative out of it. I was inspired by John Keats’ poem “This Living Hand” (which can be found in my Processing code), which deals with the life of the poet/resurrection of the poet when you read his work. I like the idea of having a machine that creates art, and since I wanted to incorporate Twitter, I decided to make a robot that writes poetry and tweets it at the push of a button. After some googling, I discovered Rita (link here), a library that can be used to intelligently parse English words/sentences. Then, I used the Gutenberg Project (link here) as well as other online poetry resources to collect .txt files filled with poetry: Shakespeare’s sonnets, Keats’ odes (+ “This Living Hand”), “Nothing but Death” by Pablo Neruda, and some works by Poe and a Russian poet named Marina Tsvetaeva. I chose them because they are my favorites, and they are very typically flower-y poets, with lots of emotional and figurative content, an interesting contrast when coming from a machine. I also used Twitter4J and learned heavily from the examples Golan posted on the EMS website.

There are two main components to my project: the Arduino code and the Processing code. The Arduino part is very simple; it detects when the button is pushed and prints the amount of electromagnetic interference (from 0-1023) to the Serial monitor. The Processing code takes the printed value, grabs a grammatically correct sentence comprised of words from the .txt files, and, according to the EMI, scrambles the sentence into a more jumbled sentence (as if it were being confused by all the interference). To randomize the sentence, each time it runs through the Randomize function, it does any of a number of random changes (reversing, shortening, adding random characters). The higher the EMI, the more scrambled the sentence becomes. Then, once it’s done scrambling, it tweets the resulting sentence to my Twitter account.

(side note: I’d like to make this wireless by adding a wifi shield, but I wonder if the wifi would mess with the EMI. Only one way to find out, so I’m gonna try it over break.)

Pics:

 

This is a window that opens in Processing when running the sketch, it has instructions on the first line, the un-randomized poem on the second line, and the final, Tweeted poem on the third line.

The video is coming soon, though it’s not very interesting to watch (just me pushing the button and refreshing the Twitter page so you can see that it Tweeted). I’m having some issues getting it off my phone.

To see the poetry the PoetBot has tweeted, see here: https://twitter.com/EMIpoetry

Here’s the Fritzing diagram:

Here’s the Arduino code, for measuring the EMI:

(it has some commented-out print statements for debugging still in it.)

const int  buttonPin = 8;    // the pin that the button is attached to
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button (0 or 1)
int lastButtonState = 0;     // previous state of the button (0 or 1)
 
int emiInPin = 5; //this pin measures the EMI using a partially exposed wire
int val = 0;
 
void setup()
{
  //receive data from button (digital) or wire (analog)
  pinMode(buttonPin, INPUT);
  pinMode(emiInPin, INPUT);
 
  //initialize serial communication, for debugging purposes:
  Serial.begin(9600);
}
 
void loop()
{
  buttonState = digitalRead(buttonPin); //read current state of button (1 or 0)
  val = analogRead(emiInPin); //read input from wire
 
  if (buttonState != lastButtonState) { //button state changed (button was pressed)
    if (buttonState == HIGH){ //button was turned on, pay attention to info from wire
      //Serial.println("button on!");
        Serial.println(val);
    }
 
    if (buttonState == LOW){//button was released, we don't care about that event
      // (we only care about triggering an event when the button is pressed,
      //  not the actual state of the button)
      // this line is actually kind of useless, but I like to include it for clarity.
      //Serial.println("button off!");
     }
    lastButtonState = buttonState; //save the button state
  }
}

 

And here’s the Processing code, for receiving the signal from Arduino, making a tweet, and tweeting it:

/*
 andrea gershuny
 carnegie mellon university, electronic media studio II
 fall 2012, section A w/ Golan Levin
 
 based on code by Golan Levin:
 processing_to_twitter processing sketch, Modified from
 visit <http://www.instructables.com/id/Simple-Tweet-Arduino-Processing-Twitter/>
 visit <http://www.twitter.com/msg_box>
 
 this reads input from the arduino that indicates the electromagnetic interference (EMI),
 uses a library called rita to generate a gramatically correct (but not necessarily semantically correct)
 sentence from texts by Keats, Shakespeare, Neruda, Tsvetaeva, and Poe, mixes that senteces up,
 and tweets it. (The more EMI, the more the poem gets mixed up.)
 
 for fun, here is my favorite poem, "This living hand, now warm and capable",
 by John Keats, written 1819-1820:
 
 This living hand, now warm and capable
 Of earnest grasping, would, if it were cold
 And in the icy silence of the tomb,
 So haunt thy days and chill thy dreaming nights
 That thou would wish thine own heart dry of blood
 So in my veins red life might stream again,
 And thou be conscience-calm’d–see here it is–
 I hold it towards you.
 
 */
 
import processing.serial.*;
 
import twitter4j.conf.*;
import twitter4j.internal.async.*;
import twitter4j.internal.org.json.*;
import twitter4j.internal.logging.*;
import twitter4j.http.*;
import twitter4j.api.*;
import twitter4j.util.*;
import twitter4j.internal.http.*;
import twitter4j.*;
 
import rita.*;
 
int MAX_LINE_LENGTH = 140;
String data = "http://rednoise.org/rita/data/";
String chars = "q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m,Q,W,E,R,T,Y,U,I,O,P,A,S,D,F,G,H,J,K,L,Z,X,C,V,B,N,M,;,.,,:,~,`,"; //chars to mess the tweet up with
static int charsLen = 60; //length of chars string
 
RiText rts[];
RiMarkov markov;
 
//these are my twitter keys, please don't steal them or be a dick or anything.
static String OAuthConsumerKey    = "BdKQbq82hpjkS6l45pDqVQ";
static String OAuthConsumerSecret = "38yctkzoONdGwZfpPABEytdhtWsPhjzlrldqPdkJvJc";
static String AccessToken         = "988448905-jiP8Seol9Ttp9qK59hdg5c15CHHN4ZJxsoWbRKUq";
static String AccessTokenSecret   = "A8udnwBIDh1u3OM2fTTO3Rp9ftKVK9ZNGDibKwv3hJI";
 
Serial arduino;
Twitter twitter = new TwitterFactory().getInstance();
 
String prevUnmodifiedPoem = "";
String prevModPoem = "";
 
void setup() {
  size(700, 150);
  loginTwitter();
 
  markov = new RiMarkov(this, 3);  //model that tokenizes based on whitespace characters
 
  println(Serial.list());
  String arduinoPort = Serial.list()[0];
  arduino = new Serial(this, arduinoPort, 9600);
  arduino.bufferUntil('\n');
 
  /* load some text files for processing to pull from:
   (1) Shakespeare's sonnets (was going to use Hamlet but Hamlet's name was distracting)
   (2) some poems by John Keats (his odes + "This Living Hand"
   (3) "Nothing but Death" (also known as "Only Death") by Pablo Neruda
   (4) some works by Edgar Allen Poe
   (5) some poems by Marina Tsvetaeva
   */
 
  markov.loadFile("shakespeare.txt");
  markov.loadFile("keats.txt");
  markov.loadFile("neruda.txt");
  markov.loadFile("poe.txt");
  markov.loadFile("tsvetaeva.txt");
}
 
void loginTwitter() {
  twitter.setOAuthConsumer(OAuthConsumerKey, OAuthConsumerSecret);
  AccessToken accessToken = loadAccessToken();
  twitter.setOAuthAccessToken(accessToken);
}
 
private static AccessToken loadAccessToken() {
  return new AccessToken(AccessToken, AccessTokenSecret);
}
 
void draw() {
  background(#E3DFC7);
  fill(0,0,0);
  text("// Hit the button and I'll write you a poem.", 20, 45);
  text(prevUnmodifiedPoem, 20, 70);
  text(prevModPoem, 20, 95);
}
 
void keyPressed() {
  String poem = markov.generateSentence();
  println(poem) ;
 
  postMsg (poem);
  delay(12000);
}
 
void serialEvent(Serial arduino) {
  String inString = arduino.readStringUntil('\n');
  int arduinoMsg = 0;
if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);
    arduinoMsg = int(inString);
    println(arduinoMsg);
 
    String poem = markov.generateSentence();
    prevUnmodifiedPoem = poem;
    String msg = randomize(poem, arduinoMsg);
    prevModPoem = msg;
    postMsg(msg);
    delay(12000);
  }
}
 
String randomize(String poem, int emiVal) {
  String[] list = split(poem, " ");
  //println(list);
  int changes = 0; //keeps track of how many changes we've made to the poem
  while (changes < emiVal) { //number of changes we'll make is based on EMI
    int change = int(random(3)); //determines which change we'll make this pass
 
    if (change==0) {
      list = reverse(list);
    }
 
    if (change==1) {
      list = sort(list);
    }
    if (change==2) {
      list = shorten(list);
    }
    if (change==4) {
      list = appendRandom(list);
    }
 
    if (change==3) {
      list = spliceRandom(list);
    }
 
    changes ++;
  }
  poem = join(list, " ");
  return poem;
}
 
String[] appendRandom(String[] list) {
  int randomLen = int(random(1, 7)); //random length of string to append
  int randomInd = int(random(1, 60)); //random index
  // String[] charsList = chars.split(",");
  String toAppend = chars.substring(randomInd, randomInd+randomLen);
  String[] list2 = append(list, toAppend);
  return list2;
}
 
String[] spliceRandom(String[] list){
 int randomLen = int(random(1,7)); //random length of string to splice
 int randomInd = int(random(1,60)); //random index
 int index = int(random(0,5)); //6 words is minimum length of sentence; this index is where we'll splice in
 String toSplice = chars.substring(randomInd,randomInd+randomLen);
 String[] list2 = splice(list, toSplice, index);
 return list2;
 }
 
void postMsg(String s) {
  try {
    Status status = twitter.updateStatus(s);
    println("new tweet --:{ " + status.getText() + " }:--");
  }
  catch(TwitterException e) {
    println("Status Error: " + e + "; statusCode: " + e.getStatusCode());
  }
}

Post a comment