Home > arduino, electronics, project > Putting Arduino Uno to work: Twitter Status Displayer micro-project

Putting Arduino Uno to work: Twitter Status Displayer micro-project

Two full weeks of Arduino experience is more fun than I initially thought, especially when working on a specific project instead of following loads of pages of tutorials. I finished working on my ‘Twitter Status displayer’, a project that monitors a Twitter account for status updates and displays a summary of the latest Tweet, along with the updated date and time on a 20×4 LCD. As an additional feature, it also handles accounts that have been protected (by using ‘Protect my tweets’ setting on Twitter) by displaying an appropriate message on the LCD.

The project is composed of two major components:

  • Arduino Component: This maintains the LCD, the board’s state, and communicates with the host component over a serial line available on the Arduino. Depending on what the current status is, it displays appropriate text on the LCD. For example, if the Twitter account is currently ‘protected’, it displays an ‘A/C Protected!’ message on the LCD. If the latest status was successfully fetched from Twitter, then it displays as much of the status text that can fit on the lcd screen.  The circuit itself is a regular 20×4 LCD circuit connected as in:
Arduino LCD Connections

http://www.instructables.com/id/Connecting-an-LCD-to-the-Arduino/

  • Python Component: Because the Arduino I have does not have the capacity to connect to the Internet, it uses a python script sitting on the host computer as a proxy to Twitter. The python script put in simple terms, is a single-threaded server to the Arduino, that connects to Twitter’s API periodically (observing rate limits of the API), and gets an update on the protection status of the account. If the account is not protected, the details of the latest tweet are also fetched. It passes on this information to the Arduino for it to process and display it on the LCD.

That being the logical overview, the nitty gritty details were quite a few: establishing a serial connection for the Arduino and host to exchange data, required passing short COMMANDs followed by any relevant information by the sender and an acknowledgement by the receiver. I ran into quite reasonable problems with the communication aspects in my initial designs, making me cook up a MSG-ACK based model. For example, when the board is connected to the host, it sends out an INIT message to the host and if proper initilialization happened on the host, it sends out an INITOK acknowledgement to the board. There are a few other such communication scenarios, as can be seen in the following code:

Arduino Source code:

/*
Bhaktavatsalam Nallanthighal
January 3 2011
This code is under public domain

Twitter status display on 20x4 LCD.
  - Username on first line
  - Status on next 2 lines.
      If status too long, we'll shorten it and display an ellipsis.
  - Updated date on fourth line
*/

#include "LiquidCrystal.h"

/* Initialize the LCD with the ports we connected it to */
LiquidCrystal lcd(12, 11, 2, 3, 4, 5);
String screen_name;
String statusTxt;
String updated;
boolean handshakeDone;
String readBuffer;

void setup() {
    /* Initialize components */
    Serial.begin(9600);
    lcd.begin(20, 4);

    /* Initialize variables */
    screen_name = "";
    statusTxt = "";
    updated = "";
    readBuffer = "";
    handshakeDone = false;

    /* Send INIT message */
    Serial.println("INIT");
    lcd.setCursor(0, 0); // row 1, column 1
    lcd.print("Waiting for host...");
}

/*
    Non-blocking-Read serial data to buffer until readline()
    If complete data not available, return empty string and keep contents
    in buffer until newline character is encountered.
*/
String readline() {
    String msg = "";
    while(Serial.available()>0) {
        char ch = Serial.read();
        readBuffer.concat(ch);
        if(ch=='\n') {
            msg = readBuffer;
            readBuffer = "";
            break;
        }
    }

    Serial.flush();
    return msg;
}

void loop() {
    String hostMsg = readline().trim();

    if(!hostMsg.equals("")) {
        if(hostMsg.equals("INITOK")) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Fetching Tweet...");
            handshakeDone = true;
        }

        if(handshakeDone) {
            if(hostMsg.equals("PROT")) {
                /* Send PROTOK msg to host */
                Serial.println("PROTOK");
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print("A/C Protected!");
            }
            else if(hostMsg.equals("NEW")) {
                screen_name = "";
                statusTxt = "";
                updated = "";

                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print("Updating Tweet...");

                /* Send NEWOK msg to host */
                Serial.println("NEWOK");
           }
           else {
                if(screen_name.equals("")) {
                    screen_name = hostMsg;
                }
                else if(statusTxt.equals("")) {
                    statusTxt = hostMsg;
                }
                else if(updated.equals("")) {
                    updated = hostMsg;
                }
                else if(hostMsg.equals("UPDATE")) {
                    lcd.clear();
                    lcd.setCursor(0, 0);
                    lcd.print("@" + screen_name.substring(0, 20));

                    lcd.setCursor(0, 1);
                    lcd.print(statusTxt.substring(0, 20));
                    lcd.setCursor(0, 2);
                    if(statusTxt.length()>20) {
                        if(statusTxt.length()<40) {
                            lcd.print(statusTxt.substring(20, statusTxt.length()));
                        }
                        else {
                            lcd.print(statusTxt.substring(20, 35) + "...");
                        }
                        lcd.setCursor(0, 3);
                    }
                    lcd.print(updated.substring(0, 20));

                    /* Indicate update success to host */
                    Serial.println("UPDATEOK");
                }
            }
        }
    }
}

Python (Windows):

# Bhaktavatsalam Nallanthighal
# January 5 2011
# This code is under public domain

import httplib
import json
import serial
import time

TWITTER_API_ENDPOINT = "api.twitter.com"
ARDUINO_PORT = "\\.\COM5"
POLL_FREQUENCY = 30 # delay to observe twitter rate limits
SCREEN_NAME = "bhaktavatsalam"

# Open HTTP connection to twitter
httpConnection = httplib.HTTPConnection(TWITTER_API_ENDPOINT)

# Open non-blocking-read, blocking-write serial connection to arduino
serialConnection = serial.Serial(ARDUINO_PORT, baudrate=9600, timeout=0)

# Close existing connections to the board and open new connection
serialConnection.close()
serialConnection.open()
serialConnection.flushInput()
serialConnection.flushOutput()

# Twitter Info
screen_name = ""
status = ""
updated = ""

def getStatus(screen_name):
	httpConnection.request("GET", "/1/users/show.json?screen_name="+screen_name)
	resp = httpConnection.getresponse()
	return resp.read()

def writeToArduino(msg):
	time.sleep(1)
	serialConnection.setDTR(level=0)
	time.sleep(1)
	serialConnection.write(msg + "\r\n")

handshakeDone = False
readBuffer = ""
lastPolled = 0
while True:
	# Buffer data from connection until a newline is encountered
	readBuffer += serialConnection.readline()
	if readBuffer!="" and readBuffer[len(readBuffer)-1]=='\n':
		readBuffer = readBuffer.strip()
		if readBuffer=="INIT":
			# Send INIT handshake
			writeToArduino("INITOK")
			handshakeDone = True
			print "ME: Initialized."
		elif readBuffer=="PROTOK":
			print "ARDUINO: Protected Msg Acknowledged"
		elif readBuffer=="NEWOK":
			writeToArduino(screen_name + "\r\n" + status + "\r\n" + updated + "\r\nUPDATE")
			print "ME: New Data Sent."
		elif readBuffer=="UPDATEOK":
			print "ARDUINO: Board Updated."
		# Clear buffer
		readBuffer = ""

	if handshakeDone==True:
		currentTime = time.time()
		if currentTime-lastPolled > POLL_FREQUENCY:
			# Fetch and parse twitter status
			resp = json.loads(getStatus(SCREEN_NAME))
			if "error" in resp:
				print "Twitter Error/User does not exist"
				exit();
			# If account is protected, no status will be available
			elif "protected" in resp:
				if resp["protected"]==True:
					# Invalidate current info
					screen_name = ""
					status = ""
					updated = ""
					writeToArduino("PROT")
					print "TWITTER: Account is protected"
				#Write to board only if a change occured
				elif screen_name!=resp['screen_name'] or status!=resp['status']['text']:
					resp['status']['created_at'] = time.strftime("%m/%d/%y %H:%M:%S", time.gmtime(time.mktime(time.strptime(resp['status']['created_at'], "%a %b %d %H:%M:%S +0000 %Y"))))
					if updated!=resp['status']['created_at']:
						screen_name = resp['screen_name']
						status = resp['status']['text']
						updated = resp['status']['created_at']

						#Tell the board that new data is available
						writeToArduino("NEW")
				else:
					print "TWITTER: No Tweet Updates"
			lastPolled = currentTime

# Close connections
serialConnection.close()
httpConnection.close()

To see it work, first make sure configuration parameters in python like the port, twitter screen name are in place. Start the python script from the command line. Then, connect the Arduino to the port configured. You should see a bunch of status messages on the command line, and the latest tweet summary on the Arduino LCD. If possible, I’ll try to post a video of mine this weekend.

  • Arduino LCD Connections

    http://www.instructables.com/id/Connecting-an-LCD-to-the-Arduino/

Categories: arduino, electronics, project Tags: ,
  1. Eshana Natesh
    January 20th, 2012 at 11:01 | #1

    Hey can you please post a video of the project. Especially running the python part.

  1. No trackbacks yet.