Watch @EmojiTetra Live on ESP32 OLED Display

@EmojiTetra is an online game resembling Tetris, hosted on Twitter platform. Every 20 minutes, the @EmojiTetra account posts a tweet that displays the current game board, along with a four-option poll that allows visitors to vote for the game's next move: left, right, down, rotate, or plummet.

a tweet by @EmojiTetra

I find this game interesting. To watch or participate in @EmojiTetra, I need to unlock my tablet, open Twitter app, search for "EmojiTetra", and scroll past the pinned tweet in order to see the current game move. In total, this process needs 17 taps. Looking at the 0.96 inch OLED display on my Heltec WiFi_Kit_32 board, I'm thinking: can I play @EmojiTetra on an ESP32?

Twitter API

Twitter offers an API that allows applications to retrieve and post tweets. GET statuses/user_timeline resource, for example, retrieves a collection of the most recent tweets posted by a specific user. To watch the game, "user timeline" is exactly what I need to retrieve the current game state.

Looking through Twitter API documentation, I determined that the full resource URI shall be https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=EmojiTetra&count=5&trim_user=1. This API call would return the latest five tweets from @EmojiTetra, and trim away the details about the account itself. However, curling the above URI directly gives me nothing more than an error message: {"errors":[{"code":215,"message":"Bad Authentication data."}]}. It turns out that, Twitter API requires authentication for retrieving anyone's tweets, even if @EmojiTetra is a public account that is visible on Twitter.com without signing in. Thus, I had to enlist a Twitter client library to help with Twitter's authentication.

One of many Twitter client libraries is python-twitter. Using this library is straightforward:

# register API consumer and generate access token on https://apps.twitter.com/
twttr = twitter.Api(consumer_key='XXXXXXXXXXXXXXXXXXXXXXXXX',
                    consumer_secret='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
                    access_token_key='88888888-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
                    access_token_secret='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
timeline = twttr.GetUserTimeline(
    screen_name='EmojiTetra', count=5, trim_user=True)

The timeline variable now has a JSON array of five tweets. Each tweet looks like this:

{
  "created_at":"Thu May 24 11:42:41 +0000 2018",
  "hashtags":[],
  "id":999616674662776834,
  "id_str":"999616674662776834",
  "in_reply_to_screen_name":"EmojiTetra",
  "in_reply_to_status_id":999611645742628865,
  "in_reply_to_user_id":842095599100997636,
  "lang":"en",
  "source":"<a href=\"http:\/\/twitter.com\/download\/iphone\" rel=\"nofollow\">Twitter for iPhone<\/a>",
  "text":"Next \u2833\u3000Score 11300\n\n\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\n\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\n\u25fd\u25fd\u25fd\u25fd\ud83d\udc9a\ud83d\udc9a\u25fd\n\u25fd\u25fd\u25fd\u25fd\ud83d\udc9a\u25fd\u25fd\n\u25fd\u25fd\u25fd\u25fd\ud83d\udc9a\u25fd\u25fd\n\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\n\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\u25fd\n\u25fd\u25fd\u25fd\ud83d\udc96\u25fd\u25fd\u25fd\n\ud83d\udc9a\ud83d\udc9a\u25fd\ud83d\udc96\u25fd\ud83d\udc9c\u25fd\n\ud83d\udc9a\ud83d\udc9a\u25fd\ud83d\udc96\u25fd\ud83d\udc9c\u25fd\n\ud83d\udc9a\ud83d\udc9a\ud83d\udc97\ud83d\udc97\ud83d\udc9b\ud83d\udc9b\u25fd",
  "urls":[],
  "user":{
    "id":842095599100997636,
    "id_str":"842095599100997636"
  },
  "user_mentions":[]
}

From Tweet to Plain Text

Each game tweet has 13 lines of text, available in text field of the JSON object:

  • Line 0 shows the next game piece, and the current score.
  • Line 1 is blank.
  • Line 2-12 represent the game board, which contains 11 rows and 7 columns.
  • \u25fd denotes an empty tile; other emojis indicate occupied tiles.

Occasionally, @EmojiTetra account would post a non-game tweet that does not conform to the above pattern. To workaround this potential issue, I retrieve five tweets instead of just one, allowing me to skip non-game tweets and hopefully still have a game tweet to work with.

Emojis are ideograms and smileys encoded in Unicode characters. It isn't easy to work with Unicode in Arduino sketch code. Furthermore, my OLED is single-colored and cannot display these colorful emojis. Therefore, I wrote a Python program to convert the tweet into a simpler format that is easier to work with from Arduino:

  • Line 0 is time since the tweet was posted, in seconds.
  • Line 1 has the current score.
  • Line 2-12 represent the game board.
  • "." denotes an empty tile; "X" denotes an occupied tile.

This program (source code at the end of this article) would convert the tweet above into:

310.280453
11300
.......
.......
....XX.
....X..
....X..
.......
.......
...X...
XX.X.X.
XX.X.X.
XXXXXX.

I further made the script into a web service, and deployed it on my "house server" (a Beaglebone Black running NGINX).

Watch the Game on OLED

The OLED unit I have is Heltec WiFi_Kit_32, a development board based on ESP32 microcontroller. The Arduino sketch (source code at the end of this article) I wrote works as follows:

  1. Connect to WiFi.
  2. Initialize U8g2 library for the OLED display.
  3. Download the text from Python web service.
  4. Draw the timestamp and current score on the top of OLED, using a 8-pixel font.
  5. Draw the game board on the bottom of OLED, using 8x8 boxes with 1-pixel gap in between.
  6. Wait 15 seconds and repeat step 3-6.

EmojiTetra game board on OLED

Watch Only? Can I Participate?

My original goal was to play @EmojiTetra on the OLED, but so far I'm only able to watch the big game. To participate in the gameplay, I would need a way to vote in a Twitter poll. However, Twitter API only allows retrieving the poll options, but does not allow voting in a poll through the API. Moreover, poll information is a "premium enrichment" that is available to paid subscription plans only. Therefore, I won't be able to play the game anytime soon without running a full browser somewhere.

Code Download

View EmojiTetra-OLED code on GitHub Gist

Download EmojiTetra-OLED code as ZIP

Note: Code has been updated on 11JUN2018 to accommodate @EmojiTetra's new tweet format.

Tags: API ESP32 Python