Toby Segaran

Experiments in electronics, data, art, cocktails and physics

Twitter lights and memory limits with Arduino Yun

When the Arduino YÚN was announced, I was definitely curious. Many people said it was overpriced because a Raspberry Pi with all the necessary add-ons was slightly cheaper and more powerful.  Although I’ve managed to do some interesting things with my Pi (more on my home automation system to come), I’ve definitely found that it’s always been a little faster to get an Arduino interfacing with external components and they’re much harder to break. Rather than explain what it’s all about (which you can find elsewhere),

I got my Yun a few days ago, delivered at work. When I got home I figured I had about an hour to play with it before I had to go meet some people for drinks. I already had soldered this colorful shield and was familiar with the Neopixel libraries so I had an option for display. I was most interested in setting it up to read some information from the web via wifi and having it change the colors on the shield to reflect some kind of updates.

The Yun comes preloaded with libraries for Temboo, which saves a lot of coding on the Linux side, and just lets you write regular Arduino sketches. They had an example for Twitter, so I decided to go with that and see if I could show my Twitter feed as a sequence of colors on the Neopixels.

If you read the example, you’ll notice that (a) it uses “string” which I hadn’t really seen in Arduino code before (and it’s not the usual C string I’m used to) and (b) it only retrieves one tweet. It took some effort to figure out how to make Temboo retrieve more than one tweet. You’d think you could just update this line:

HomeTimelineChoreo.addInput("Count", "1");

Unfortunately, changing this to more doesn’t work because these lines:

HomeTimelineChoreo.addOutputFilter("tweet", "/[1]/text", "Response");
HomeTimelineChoreo.addOutputFilter("author", "/[1]/user/screen_name", "Response");

Only fill one of the variables no matter how many you retrieve. I created a loop which fills in the filters the way Temboo expects them though.

The next problem I had was although I was trying to retrieve 40 tweets (the number of lights on the shield), I was only getting 7 back. It took me a while to understand why, but then it occurred to me — the ATMega was running out of RAM. Since I’d never actually tried text processing on a microprocessor this small before, I hadn’t expected this.

So I removed the line that retrieved the tweets and just retrieved the author names. This allowed me to get about 15-20 results, but still not enough to fill all my pixels. As a guess, I assumed that Temboo was able to get all the results (the Linux side having a lot more RAM), but they got packaged and sent to the Arduino which didn’t have enough RAM to handle it. To work around this, I kept the count at 40 but ran the request 4 times and had a different set of output filters each time (the first 10, the second 10, etc.). And it worked!

Twitter lights

(if you’ve tried to photograph fully lit LEDs before you’ll understand that this photo was actually taken in daylight and the shield is difficult to look directly at)

Each light represents a tweet, and the color is generated from the first letter of the authors username. Once I realized the pitfalls, it was pretty easy to get something cool built in a little over an hour (although when debugging a twitter app, hitting rate limits is a problem)

Here’s the code if you’re curious. Notice how the same thing is retrieved 4 times but with different output filters:

#include <Bridge.h>
#include <Temboo.h>
#include <Adafruit_NeoPixel.h>
#include "TembooAccount.h" // contains Temboo account information
                           // as described in the footer comment below

// Note that for additional security and reusability, you could
// use #define statements to specify these values in a .h file.
const String TWITTER_ACCESS_TOKEN = "";
const String TWITTER_ACCESS_TOKEN_SECRET = "";
const String TWITTER_CONSUMER_KEY = "";
const String TWITTER_CONSUMER_SECRET = "";

int numRuns = 1;   // execution count, so this doesn't run forever
int maxRuns = 10;   // the max number of times the Twitter HomeTimeline Choreo should run

Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, 6, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.show(); 
  delay(4000);
  Bridge.begin();
}
void loop()
{
  for (int outer=0;outer<5;outer++) {
    TembooChoreo HomeTimelineChoreo;

    HomeTimelineChoreo.begin();

    HomeTimelineChoreo.setAccountName(TEMBOO_ACCOUNT);
    HomeTimelineChoreo.setAppKeyName(TEMBOO_APP_KEY_NAME);
    HomeTimelineChoreo.setAppKey(TEMBOO_APP_KEY);

    HomeTimelineChoreo.setChoreo("/Library/Twitter/Timelines/HomeTimeline");

    HomeTimelineChoreo.addInput("Count", "40"); // the max number of Tweets to return from each request
    HomeTimelineChoreo.addInput("AccessToken", TWITTER_ACCESS_TOKEN);
    HomeTimelineChoreo.addInput("AccessTokenSecret", TWITTER_ACCESS_TOKEN_SECRET);
    HomeTimelineChoreo.addInput("ConsumerKey", TWITTER_CONSUMER_KEY);    
    HomeTimelineChoreo.addInput("ConsumerSecret", TWITTER_CONSUMER_SECRET);

    for (int i=1;i<=8;i++) {
      HomeTimelineChoreo.addOutputFilter(String(i+(outer*8)), "/["+String(i+(outer*8))+"]/user/screen_name", "Response");
    }

    unsigned int returnCode = HomeTimelineChoreo.run();

    if(returnCode == 0) {

      while(HomeTimelineChoreo.available()) {
        // read the name of the output item
        String name = HomeTimelineChoreo.readStringUntil('\x1F');
        name.trim();

        // read the value of the output item
        String data = HomeTimelineChoreo.readStringUntil('\x1E');
        data.trim();

        // assign the value to the appropriate String
        int col = ((int)data.charAt(0)*10) % 256;
        uint32_t wc =  Wheel((byte) col);
        strip.setPixelColor(name.toInt()-1, wc);
      }

      strip.show();
    } else {
      // there was an error
      // print the raw output from the choreo
      while(HomeTimelineChoreo.available()) {
        char c = HomeTimelineChoreo.read();
        Serial.print(c);
      }
    }

    HomeTimelineChoreo.close();

  }

  delay(15*60000); // wait 90 seconds between HomeTimeline calls
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

If you have questions about the code, put them in the comments so I can answer them for everyone!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: