Making a Reddit Upvote Notifier

The Arduino Yún's ability to connect to the Internet introduces many new possibilities to traditional Arduino development, such as capturing data from the web and making use of it inside of a sketch.

This project will demonstrate this ability by monitoring the number of upvotes accrued by a Reddit comment thread and alerting the user when new upvotes are added.

Many companies offer application programming interfaces (APIs, for short) that allow third-party programs to access their data. Reddit, for example, provides an API that gives detailed information and statistics for posts, comment threads, and users.

This project uses a Python script on the Yun's Linux side to read the current upvote count from a comment thread using Reddit's API. A skech running on the Arduino side will track this number and display a notification message on an LED matrix every time a new vote is added.

To complete this project, you need:

  1. An Arduino Yún, connected to your local network over Ethernet or Wi-Fi.

  2. A microSD card, with the OpenWrt-Yun Linux file system expanded onto it.

  3. An Adafruit Mini 0.8" or 1.2" 8x8 LED Matrix with I2C backpack.

  4. A solderless breadboard and jumper wires.

Using the Reddit API

Reddit's API documentation provides a reference for all the different types of HTTP requests that can be performed to retrieve data from their site.

The API allows users and applications to access all the data that pertains to a comment thread simply by adding ".json" to the end of the thread's URL.

To test this out, navigate to a Reddit comment thread in your browser. For example: http://www.reddit.com/r/space/comments/2t7r8v/its_the_earth_thats_moving/

Next, navigate to the same URL, but this time with an added ".json" at the end: http://www.reddit.com/r/space/comments/2t7r8v/its_the_earth_thats_moving/.json

Viewing the ".json" URL in your browser will return a large block of text which is formatted in JavaScript Object Notation (JSON), a structured text format that is commonly used to exchange data across the web.

The JSON data returned here contains all of the Reddit thread's comments, usernames, and upvote counts. The data can be parsed out using Python and the JSON module, which can convert JSON text into a Python object that is easier to work with.

This project will use a Python script called upvotes.py that takes in any comment thread URL as in input, and prints out its upvote count.

To create this script on the Yún's microSD card, follow these steps:

  1. Login to the OpenWrt-Yun command line.

  2. To create a new file, type the following command and then press Enter:

    nano /mnt/sda1/upvotes.py

  3. Add the following code to the file:

    #!/usr/bin/python
    import urllib2
    import json
    import sys

    url = sys.argv[1] + ".json"
    response = urllib2.urlopen(url)
    data = json.load(response)
    upvotes = data[0]['data']['children'][0]['data']['score']

    print upvotes

  4. Press Ctrl + X.

  5. Press Y and then press Enter.

Test the script from the command line by passing it a Reddit comment URL string as an argument. For example, type the following on a single line and then press Enter:

/mnt/sda1/upvotes.py "http://www.reddit.com/r/space/comments/2t7r8v/its_the_earth_thats_moving/"

The comment URL's upvote count will be printed out to the command line.

The script begins by importing the necessary Python modules:

import urllib2
import json
import sys

The urllib2 and json modules contain methods for fetching information over the web through hypertext transfer protocol (HTTP), and for converting JSON data into a Python object.

The sys module provides access to many of the basic constants and functions of the Python interpreter. In this case, it will give the script access to the URL passed into it as a command-line argument. Arguments passed to the script at the command line are stored in a list called sys.argv.

The first item in this list, sys.argv[0], simply contains the name of the function itself. The subsequent list items contain the command-line arguments passed to the function when it started: sys.argv[1], sys.argv[2], etc.

The variable url is defined as the input URL string (provided to the script as an argument at runtime) with an added ".json" to the end of it.

url = sys.argv[1] + ".json"

JSON data from the URL is loaded and converted into a format that's usable in Python, contained in the variable data.

response = urllib2.urlopen(url)
data = json.load(response)

The actual upvote score itself is buried deep within the data object, but can be accessed directly at data[0]['data']['children'][0]['data']['score']. The count is stored in the variable upvotes.

upvotes = data[0]['data']['children'][0]['data']['score']

The script finishes by printing upvotes, which will make it accessible to the Arduino sketch the script will be called from.

print upvotes

Accessing Upvotes from an Arduino Sketch

The project will need to repeatedly call this Python script in order to monitor the URL's upvote count. This can be accomplished using Arduino's Bridge library and Process class.

First, open the Arduino IDE and create a new sketch called UpvoteNotifer. Add the following directive at the top of the file:

#include <Bridge.h>

The Bridge library is a collection of classes and functions that allow the Yún's two processors to talk to each other.

Next, define a string variable that stores the URL for the comment thread to monitor:

String url = "http://www.reddit.com/r/space/comments/2t7r8v/its_the_earth_thats_moving/";

Then in the sketch's setup() function start the Serial and Bridge libraries:

void setup() {
  Serial.begin(9600);
  Bridge.begin();
}

In the sketch's loop() function, add the following code:

void loop() {
  Serial.println(getUpvotes());
  delay(10000);
}

Finally, add a function called getUpvotes() to your sketch:

int getUpvotes() {
  Process p;
  p.runShellCommand("/mnt/sda1/upvotes.py " + url);
  String str = p.readStringUntil('\n');
  p.close();
  return str.toInt();
}

Upload this sketch to your Arduino and open the Serial monitor. The sketch will print out the thread's upvote count every 10 seconds.

The getUpvotes() function begins by creating an instance of the Process class called p, which allows the sketch to start shell scripts or Python programs on the Linux side of the Yún.

Process p;

Next, the runShellCommand() method is used to run upvotes.py. The entire command, including the URL argument, is passed into this method as a string.

p.runShellCommand("/mnt/sda1/upvotes.py " + url);

The readStringUntil() method reads the printed response from the Python script one byte at a time until a "\n" newline character is received. This response will be our upvote count which is stored as a String object str.

String str = p.readStringUntil('\n');

The function finishes by closing the Process obect p and returning the upvote count converted to an integer.

p.close();
return str.toInt();

getUpvotes() is being called repeatedly, every 10 seconds, by the sketch's loop() function. With each call the output of the function is being printed to the serial monitor.

Serial.println(getUpvotes());
delay(10000);

Tracking New Upvotes

In order to track the URL's upvote count and identify when new upvotes are added the sketch will require some additional logic.

First, declare a variable called currentUpvotes below the #include statement at the top of your sketch and initialize it to 0.

int currentUpvotes = 0;

Then add the following two lines to the end of the setup() function:

currentUpvotes = getUpvotes();
delay(10000);

When the setup() function runs currentUpvotes will now hold an initial upvote value returned by the getUpvotes() function. This number will establish a baseline count when the sketch first begins.

Next, create a function called notify() to print a message when our comment thread receives a new upvote:

void notify() {
  String msg = "!!! New Upvote Added !!! Current Upvotes: " + String(currentUpvotes);
  Serial.println(msg);
}

Delete the contents of the loop() function and replace it with the following code:

void loop() {
  int newUpvotes = getUpvotes();
  int diff = newUpvotes - currentUpvotes;

  for (int i=0; i < diff; i++) {
    currentUpvotes++;
    notify();
  }

  currentUpvotes = newUpvotes;
  delay(10000);
}

Here, an up-to-date upvote count is collected into the variable newUpvotes and compared against the previous count which is held within currentUpvotes.

The difference diff between the two counts is calculated, and equals the number of new upvotes added to the thread between loops.

A for loop is used to repeatedly call the notify() function for each new upvote. It also increments the currentUpvotes with each iteration so that the notification message will print out the correct number.

Finally, currentUpvotes is reset to be equal to newUpvotes to preserve the latest count to be compared against during the next iteration of the main loop() function.

Upload this sketch to your Arduino and open the Serial monitor. The sketch will print out the notification message every time a new upvote is added to the comment thread.

Building the Circuit and Finishing the Sketch

To complete this project the notification message will be displayed using an Adafruit 8x8 LED matrix with I2C backpack.

Two libraries need to be installed to connect the I2C backpack:

  1. Download the Adafruit LED Backpack Library from GitHub at https://github.com/adafruit/Adafruit-LED-Backpack-Library/archive/master.zip.

  2. In the Arduino IDE, on the Sketch menu, click Include Library, and then Add Zip Library.

  3. Find the Adafruit-LED-Backpack-Library-master.zip file that was downloaded and click Open.

  4. Download the Adafruit GFX Library from GitHub at https://github.com/adafruit/Adafruit-GFX-Library/archive/master.zip.

  5. In the Arduino IDE, on the Sketch menu, click Include Library, and then Add Zip Library.

  6. Find the Adafruit-GFX-Library-master.zip that was downloaded file and click Open.

For more information, Adafruit has a detailed guide to installing the I2C backpack and Arduino libraries.

The Adafruit 8x8 LED matrix connects using four pins. These pins are labelled +, -, D, and C.

Pin Description
+ Power supply. Connect this to the Arduino's 5V pin.
- Ground. Connect this to an Arduino GND pin.
D I2C data pin (SDA). Connect this to digital pin 2 on the Arduino.
C I2C clock pin (SCL). Connect this to digital pin 3 on the Arduino.

To connect the matrix:

  1. Disconnect the Arduino from its power source.

  2. Gently, but firmly, press the header pins on the LED matrix into a breadboard.

  3. Connect jumper wires between the pin headers on the LED matrix and the Arduino.

  4. Reconnect the Arduino's power supply.

Add three lines to the beginning of your sketch to include the Adafruit libraries and the Wire library:

#include <Wire.h>
#include <Adafruit_LEDBackpack.h>
#include <Adafruit_GFX.h>

Then add the following line to create an instance of the LED matrix:

Adafruit_8x8matrix matrix = Adafruit_8x8matrix();

Add the following lines to the end of the notify() function:

for (int x=8; x>=-300; x--) {
  matrix.clear();
  matrix.setCursor(x,0);
  matrix.print(msg);
  matrix.writeDisplay();
  delay(50);
}

matrix.clear();
matrix.writeDisplay();

Finally, add these lines inside the setup() function:

matrix.begin(0x70);
matrix.setTextSize(1);
matrix.setTextWrap(false);
matrix.setTextColor(LED_ON);
matrix.clear();
matrix.writeDisplay();

This additional code will scroll the notification message across the 8x8 matrix. Upload the completed sketch to your Arduino.

Source Code

UpvoteNotifier.ino

upvotes.py

Start Building Today

book cover Start Reading Now

Your guide to Yún developement. Arduino Meets Linux includes:

  • Over 320+ pages of content
  • OpenWrt-Yun Linux tutorials
  • Python and shell scripting
  • 7 in-depth Arduino projects

Take your Arduino projects to the next level. This book shows you how.