Introduction: Twitter Mention Mood Light

Twitter Mention Mood Light -- a mood light that alerts you when @username is mentioned on Twitter.

This is a simple intro in how to control your Arduino from Twitter. If you are new to Arduino Twitter / Arduino Processing Twitter / Arduino Python Twitter / Twitter Mood Light, then please refer to simpleTweet_00_processing  and simpleTweet_01_python (and How to install Python packages on Windows 7 ) for a crash course.


Step 1: Concept: Arduino {Processing / Python} Twitter

The Arduino Twitter mood light has been around for a few years. I made one in early 2010 and recently decided to update it for Twitter OAuth protocol (step 5.) This instructable has both the [ Arduino Processing Twitter ] and [ Arduino Python Twitter ] versions.

The general idea: You've got a LED generating a peaceful glow, cycling through a bunch of colors, and meanwhile you've got Processing or Python listening to Twitter for any mention of @yourUsername. When it finds a Twitter status with @yourUsername it writes to Arduino over Serial and Arduino changes the LED from peaceful glow to alert.

The circuit board (step 6) has two buttons: Reset and Send. The reset button changes the LED from "alert" back to "peaceful glow." The send button sends a Twitter status update to your Processing or Python layer, and from there on up to Twitter. When pressing the SEND button, hold it down until you see the flash of WHITE LIGHT (approx 1 second.)  This confirms the button push has been read by Arduino.

There is some confusion about Twitter Rate Limits . The Rate Limit is the number of times Twitter will let you hit it's servers per hour, currently 350 hits per hour. Go over that number and Twitter will block you. I made the same provision for this in both Processing and Python: wait 15 seconds between hits. In Processing, in void draw() , see delay(15000); . In the Python, in the while clause, see time.sleep(15).  This does not affect the timing of any button pushing or Serial calls.

The Arduino code is the same for both Processing and Python except when Arduino listens to Serial. Both Processing and Python pass an integer value of 1 (arduino.write(1) ) but Arduino receives a different value (numeric / ascii) from each, which is why you'll see these two lines in the Arduino code:

if (serialMsg == 1) state = "mention"; // processing
    if (serialMsg == 49) state = "mention"; // python


This is because I'm not sure how to pass a string value of "mention " over Serial in Python. It's not critical, so I'll leave working as above, but if anyone has a solution for sending a string (arduino.write("mention") ), particularly in Python, please post in the comments below. 

The center of this project is the getMentions() function. It's interesting to compare how they look in Processing and in Python.

// Processing (java)
// search for any mention of @yourUsername
void getMention() {
  List mentions = null;
  try {
    mentions = twitter.getMentions();
  }
  catch(TwitterException e) {
    println("Exception: " + e + "; statusCode: " + e.getStatusCode());
}
  Status status = (Status)mentions.get(0);
  String newID = str(status.getId());
  if (oldID.equals(newID) == false){
    oldID = newID;
    println(status.getText()+", by @"+status.getUser().getScreenName());
   arduino.write(1); // arduino gets 1
  }
}

# Python
# search for any mention of @yourUsername
def getMention():
    status = api.GetReplies()
    newID = str(status[0].id)
    global oldID
    if (newID != oldID):
        oldID = newID
        print status[0].text+", by @"+status[0].user.screen_name
        arduino.write(1) # arduino gets 49

In both cases the id value received is in "long" format. By converting from "long" to "string" with str() we're able to compare the value and act on it. I had trouble doing that in long format. 

Step 2: Arduino

// twitterMentionMoodLight_arduino
// for use as is with either:
// twitterMentionMoodLight_processing
// twitterMentionMoodLight_python
//
// Generate a peaceful glow until someone on twitter mentions you.
//
// Requires a circuit with: two buttons and a pwm rgb led light, and
// 3 resistors at 220 ohm; 2 resistors at 100 ohm; 2 resistors at 10k ohm.
//
// Shout out to Tom Igoe, Adafruit, lurkers and msg boards everywhere.
// learn more at: https://www.instructables.com/member/pdxnat/
/*
#####################################################################
--------------- TWO BUTTONS AND A LIGHT ----------------------------
Button One: Hold until WHITE LIGHT; Sends a message to Twitter.
The Light: Glow peacefully until commanded by Twitter to change.
Button Two: Resets LED to peacefulGlow.

peacefulGlow() - the default state of the mood light
mention() - someone has mentioned @yourUsername
buttonSend() - update Twitter status
buttonReset() - return to peacefulGlow()
--------------------------------------------------------------------
#####################################################################
*/

const int rButton = 10; // reset button
int reset_btn_val = 0;
const int sButton = 11; // send button
int send_btn_val = 0;
String state = "peacefulGlow";

int ledAnalogOne[] = {3, 5, 6}; // PWM RGB LED pins
// Analog LED 3 = redPin, 5 = greenPin, 6 = bluePin

// Defined Colors
const byte BLACK[] = {0, 0, 0};
const byte WHITE[] = {255, 255, 255};
const byte RED[] = {255, 0, 0};
const byte GREEN[] = {0, 255, 0};
const byte BLUE[] = {0, 0, 255};
const byte ORANGE[] = {83, 4, 0};
const byte YELLOW[] = {255, 255, 0};
const byte MAGENTA[] = {255, 0, 255};

void setup(){ // begin
Serial.begin(9600);
pinMode(rButton, INPUT);
pinMode(sButton, INPUT);
for(int i = 0; i < 3; i++){ // set the 3 LED pins as outputs
pinMode(ledAnalogOne[i], OUTPUT);
}
}

void loop(){
listenToSerial();
buttonSend();
buttonReset();
setState(state);
}
void setState(String s){
if (s == "peacefulGlow") peacefulGlow();
if (s == "mention") mention();
}
void listenToSerial(){ // Twitter commands enter here
int serialMsg = 0;
if (Serial.available()){
serialMsg = Serial.read();
if (serialMsg == 1) state = "mention"; // processing
if (serialMsg == 49) state = "mention"; // python
}
}
void buttonSend(){ // Twitter posts sent here
send_btn_val = digitalRead(sButton);
if (send_btn_val == HIGH){
Serial.print("#peacefulGlow");
delay(200);
sent();
}
}
void buttonReset(){
reset_btn_val = digitalRead(rButton);
if (reset_btn_val == HIGH){
state = "peacefulGlow";
}
}
void peacefulGlow(){
state = "peacefulGlow";
if (state == "peacefulGlow") {
fadeToColor(ledAnalogOne, RED, BLUE, 6);
} else {
setState(state);
}
listenToSerial();
buttonSend();
if (state == "peacefulGlow") {
fadeToColor(ledAnalogOne, BLUE, GREEN, 6);
} else {
setState(state);
}
listenToSerial();
buttonSend();
if (state == "peacefulGlow") {
fadeToColor(ledAnalogOne, GREEN, YELLOW, 6);
} else {
setState(state);
}
listenToSerial();
buttonSend();
if (state == "peacefulGlow") {
fadeToColor(ledAnalogOne, YELLOW, ORANGE, 6);
} else {
setState(state);
}
listenToSerial();
buttonSend();
if (state == "peacefulGlow") {
fadeToColor(ledAnalogOne, ORANGE, RED, 6);
} else {
setState(state);
}
listenToSerial();
buttonSend();
}
void mention(){
state = "mention";
if (state == "mention"){
fadeToColor(ledAnalogOne, RED, BLACK, 1);
} else {
setState(state);
}
listenToSerial();
buttonSend();
buttonReset();
if (state == "mention"){
fadeToColor(ledAnalogOne, BLACK, RED, 0);
} else {
setState(state);
}
listenToSerial();
buttonSend();
buttonReset();
}
void sent(){
setColor(ledAnalogOne, WHITE);
delay(500);
}

// *************************************************************
// ***** COLOR FUNCTIONS - DO NOT TOUCH ***********

void setColor(int* led, byte* color){
for(int i = 0; i < 3; i++){
analogWrite(led[i], 255 - color[i]);
}
}

void setColor(int* led, const byte* color){
byte tempByte[] = {
color[0], color[1], color[2]};
setColor(led, tempByte);
}

void fadeToColor(int* led, byte* startColor, byte* endColor, int fadeSpeed){
int changeRed = endColor[0] - startColor[0];
int changeGreen = endColor[1] - startColor[1];
int changeBlue = endColor[2] - startColor[2];
int steps = max(abs(changeRed),max(abs(changeGreen), abs(changeBlue)));

for(int i = 0 ; i < steps; i++){
byte newRed = startColor[0] + (i * changeRed / steps);
byte newGreen = startColor[1] + (i * changeGreen / steps);
byte newBlue = startColor[2] + (i * changeBlue / steps);
byte newColor[] = {newRed, newGreen, newBlue};
setColor(led, newColor);
delay(fadeSpeed);
}
setColor(led, endColor);
}

void fadeToColor(int* led, const byte* startColor, const byte* endColor, int fadeSpeed){
byte tempByte1[] = {startColor[0], startColor[1], startColor[2]};
byte tempByte2[] = {endColor[0], endColor[1], endColor[2]};
fadeToColor(led, tempByte1, tempByte2, fadeSpeed);
}

Step 3: Processing

Install twitter4j (Processing)

You'll need to install the twitter4j library so it can be used by Processing.
Get it here: http://twitter4j.org/en/index.html#download
Install it here (or equivalent): C:\Program Files\processing-1.5.1\modes\java\libraries
You're done.
Access it here: Processing > Sketch > Import Library... > twitter4j
And when you do, it'll add this to the top of your code:
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.*;
Incidentally, you'll also add Serial I/O from the Sketch > Library, but that's not important right now.

So why do you need twitter4j? The short answer is that it provides you with simple functionality so you don't have to write a whole bunch of crazy code every time you want to access Twitter. We use twitter4j because it's awesome and it makes our job easier. 

// ################################################################
// ################################################################

// twitterMentionMoodLight_processing
// for use w/ twitterMentionMoodLight_arduino
//
// Communicate w/ arduino over Serial
// Communicate w/ twitter via twitter4j library
// Shout out to Adafruit, twitter4j, lurkers and msg boards everywhere.
// learn more at https://www.instructables.com/member/pdxnat/
// http://twitter4j.org/en/javadoc/twitter4j/Twitter.html

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.*;

static String OAuthConsumerKey = "YOUR CONSUMER KEY";
static String OAuthConsumerSecret = "YOUR CONSUMER SECRET";
static String AccessToken = "YOUR ACCESS TOKEN";
static String AccessTokenSecret = "YOUR ACCESS TOKEN SECRET";

Serial arduino;
Twitter twitter = new TwitterFactory().getInstance();

String oldID = "";

void setup() {
size(125, 125);
frameRate(10);
background(0);
println(Serial.list());
String arduinoPort = Serial.list()[0];
arduino = new Serial(this, arduinoPort, 9600);
loginTwitter();
}

void loginTwitter() {
twitter.setOAuthConsumer(OAuthConsumerKey, OAuthConsumerSecret);
AccessToken accessToken = loadAccessToken();
twitter.setOAuthAccessToken(accessToken);
}

private static AccessToken loadAccessToken() {
return new AccessToken(AccessToken, AccessTokenSecret);
}

void draw() {
background(0);
text("@msg_box", 35, 65);
listenToArduino();
getMention();
delay(15000); // wait 15 seconds to avoid Twitter Rate Limit
}

void listenToArduino() {
String msgOut = "";
String arduinoMsg = "";
if (arduino.available() >= 1) {
arduinoMsg = arduino.readString();
msgOut = arduinoMsg+" at "+hour()+":"+minute()+":"+second()+" "+year()+month()+day();
updateStatus(msgOut);
}
}

void getMention() {
List mentions = null;
try {
mentions = twitter.getMentions();
}
catch(TwitterException e) {
println("Exception: " + e + "; statusCode: " + e.getStatusCode());
}
Status status = (Status)mentions.get(0);
String newID = str(status.getId());
if (oldID.equals(newID) == false){
oldID = newID;
println(status.getText()+", by @"+status.getUser().getScreenName());
arduino.write(1); // arduino gets 1
}
}

void updateStatus(String s) {
try {
Status status = twitter.updateStatus(s);
println("new tweet --:{ " + status.getText() + " }:--");
}
catch(TwitterException e) {
println("Status Error: " + e + "; statusCode: " + e.getStatusCode());
}

Step 4: Python

If you don't know How to install Python Packages on Windows 7 then you *really * need to follow that link. It's my instructable. It's a set of directions, not explanations. You'll need to do that before you can use this code here. 

You've got Python installed (w/ help from the link above) and now you need to get your packages in order.
Python - get the Python 2.7.2 Windows Installer from http://www.python.org/download/
pySerial - http://pyserial.sourceforge.net/
simplejson - http://pypi.python.org/pypi/simplejson
httplib2 - http://code.google.com/p/httplib2/
python-oauth2 - https://github.com/simplegeo/python-oauth2
python-twitter - http://code.google.com/p/python-twitter/

Refer to my How to install Python Packages on Windows 7 instructable if you need help installing these.

To run a python script, open it in IDLE and hit "Run > Run Module".
To learn about YOUR TWITTER KEY, goto the next step. 

#######################################################################
#######################################################################
# twitterMentionMoodLight_python.py
# for use with twitterMentionMoodLight_arduino
# visit my instructables for more information
# https://www.instructables.com/member/pdxnat/
# http://python-twitter.googlecode.com/hg/doc/twitter.html
# http://dev.twitter.com/pages/rate_limiting_faq

print '<twitterMentionMoodLight>'

# import libraries
import twitter
import serial
import time

# connect to arduino via serial port
arduino = serial.Serial('COM4', 9600, timeout=1)

# establish OAuth id with twitter
api = twitter.Api(consumer_key='YOUR_CONSUMER_KEY',
                  consumer_secret='YOUR_CONSUMER_SECRET',
                  access_token_key='YOUR_ACCESS_TOKEN_KEY',
                  access_token_secret='YOUR_ACCESS_TOKEN_SECRET')

oldID = "" # used in getMention()

# listen to arduino
def listenToArduino():
    msg=arduino.readline()
    if msg > '':
        print 'arduino msg: '+msg.strip()
        updateStatus(msg.strip())

# search for any mention of @yourUsername
def getMention():
    status = api.GetReplies()
    newID = str(status[0].id)
    global oldID
    if (newID != oldID):
        oldID = newID
        print status[0].text+", by @"+status[0].user.screen_name
        arduino.write(1) # arduino gets 49

# post new message to twitter
def updateStatus(newMsg):
    localtime = time.asctime(time.localtime(time.time()))
    tweet = api.PostUpdate(newMsg+", "+localtime)
    print "tweeted: "+tweet.text

while 1:
    listenToArduino()
    getMention()
    time.sleep(15) # avoid twitter rate limit

Step 5: Twitter

* Create a Twitter account twitter.com
* Register an application with Twitter https://dev.twitter.com/apps/new

Create a Twitter account

You've probably already got one (or twelve) but for the absolute noob, take heart. It's easy.
Goto twitter.com and set up an account. Make it a public account, that way if you ever want your friends to look at it they don't have to go through a whole bunch of hooey to get there. And don't post personal stuff like "Out of town, leaving diamonds on back porch."

You'll see people using the "@" and "#" symbols a lot. Put @ before a username and # before a concept. For example, if you post the following tweet "I enjoyed the @msg_box simpleTweet_00 on @instructables #goodtimes" then that tweet will get sent to my feed (and to instructables's feed too) and it'll get added to a cache of all posts that have ever used the phrase "#goodtimes." There's more, but it's not within the scope of this lesson. Play with it. You'll figure it out.

Register an application with Twitter

What does that even mean? Relax. Think of it this way: you're creating a set of special high-tech usernames and passwords so the stuff you make can access Twitter. It's like Twitter is this big castle with a front door for all the human guests and a back door for all the automated service personnel. These high-tech usernames and passwords are called OAuth. OAuth lets your device access Twitter through the service entrance.

Having already created my Twitter account @msg_box I then went here https://dev.twitter.com/apps/new to get my OAuth password info. Review the three attached images of the forms .

First Page: Make sure you check "Client " and not "Browser", and that you allow "Read & Write ."
Second Page: Here's where you find your CONSUMER KEY and your CONSUMER SECRET
Third Page: Here's where you find your ACCESS TOKEN and your ACCESS TOKEN SECRET

You will need this information when your program logs into Twitter (via the service entrance.) In the Python script simpleTweet_01_python you will enter this information in here:
api = twitter.Api(consumer_key='YOUR_CONSUMER_KEY',
                            consumer_secret='YOUR_CONSUMER_SECRET',
                            access_token_key='YOUR_ACCESS_TOKEN_KEY',
                            access_token_secret='YOUR_ACCESS_TOKEN_SECRET')


That's it. Not hard at all.  

Step 6: Circuit Board

This is a great board for all-purpose prototyping. Two buttons and a PWM RGB LED. For this configuration, the LED must be PWM to work.


Step 7: In Conclusion...

This Twitter Mention Mood Light is not particularly unique. You'll find other examples across the internets. But what makes this special is that it's up to date with Twitter OAuth and you've got code comparisons in Processing and Python. 

If you are new to Arduino Twitter / Arduino Processing Twitter / Arduino Python Twitter / Twitter Mood Light, then please refer to simpleTweet_00_processing and simpleTweet_01_python (and How to install Python packages on Windows 7 ) for a crash course.

Adafruit Make It Tweet Challenge

Participated in the
Adafruit Make It Tweet Challenge