{"id":8832,"date":"2019-09-09T09:18:59","date_gmt":"2019-09-09T08:18:59","guid":{"rendered":"https:\/\/dev.adambowie.com\/?p=8832"},"modified":"2023-08-29T12:38:09","modified_gmt":"2023-08-29T11:38:09","slug":"news-twitter-feeds-and-inky-what-e-ink-display","status":"publish","type":"post","link":"https:\/\/dev.adambowie.com\/blog\/2019\/09\/news-twitter-feeds-and-inky-what-e-ink-display\/","title":{"rendered":"News Twitter Feeds and Inky WHAT E-Ink Display"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-photo is-provider-flickr wp-block-embed-flickr\"><div class=\"wp-block-embed__wrapper\">\n<a href=\"https:\/\/www.flickr.com\/photos\/adambowie\/48687527191\/\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/live.staticflickr.com\/65535\/48687527191_e9e8c9c73f.jpg\" alt=\"Twitter News\" width=\"500\" height=\"333\" \/><\/a>\n<\/div><\/figure>\n\n\n\n<p><em>See update at the bottom if you&#8217;ve come to this from Google.<\/em><\/p>\n\n\n\n<p>For some time now, I&#8217;d been meaning to create a little display that shows me the latest news. I&#8217;d had a Pimoroni Inky PHAT kicking around for a while, but it&#8217;d had broken. <\/p>\n\n\n\n<p>For complicated reasons, I ended up with one of Pimoroni&#8217;s larger <a href=\"https:\/\/shop.pimoroni.com\/products\/inky-what\">Inky WHAT<\/a> displays. These are 4.2&#8243; displays that have 400 x 300 pixels. They use e-ink which means that images stay on them even when they&#8217;re not powered.<\/p>\n\n\n\n<p>This is basically a how-to, and assumes that while you&#8217;re competent with technology, you may not be a Raspberry Pi expert. I&#8217;m definitely not! <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Requirements<\/h4>\n\n\n\n<p>For this project you will need:<\/p>\n\n\n\n<ul>\n<li><a href=\"https:\/\/shop.pimoroni.com\/products\/raspberry-pi-zero-w\">Raspberry Pi Zero W<\/a> (i.e. the WiFi version), with GPIO ports. Since my soldering still isn&#8217;t up to scratch, I bought one <a href=\"https:\/\/shop.pimoroni.com\/products\/raspberry-pi-zero-wh-with-pre-soldered-header\">pre-soldered<\/a>. <\/li>\n\n\n\n<li><a href=\"https:\/\/shop.pimoroni.com\/products\/inky-what\">Pimoroni Inky WHAT<\/a>. I used a black\/white\/red version, but you could save a little money and use a black\/white version.<\/li>\n\n\n\n<li>Keyboard\/Mouse\/Monitor to set everything up. You may need HDMI and micro USB adaptors.<\/li>\n\n\n\n<li>Micro SD power supply. Just about any old phone charger will do.<\/li>\n\n\n\n<li><a href=\"https:\/\/amzn.to\/2zJW52E\">MicroSD card<\/a>. 8GB is plenty, although 16GB is pretty cheap these days. Plus an SD\/microSD card reader.<\/li>\n<\/ul>\n\n\n\n<p>You could also build a case for the whole thing, or 3D print one! As the photo above shows, I haven&#8217;t.<\/p>\n\n\n\n<p>You will also need to <a href=\"https:\/\/developer.twitter.com\/en\/apps\">register with Twitter<\/a> to create keys to use their API.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Desired Outcome<\/h4>\n\n\n\n<p>In this instance, given the size the of the screen, I wanted to display the most recent Tweet from three different news organisations&#8217; Twitter accounts. In my case, the <a href=\"https:\/\/twitter.com\/BBCBreaking\">BBC Breaking news<\/a> account, <a href=\"https:\/\/twitter.com\/nytimes\">the New York Times<\/a> main account, and <a href=\"https:\/\/twitter.com\/Reuters\">the Reuters<\/a> main account.<\/p>\n\n\n\n<p>I would update the e-ink display &#8211; which appears much like a Kindle screen &#8211; every 15 minutes. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Coding<\/h4>\n\n\n\n<p>To display the information, I needed to grab the most recent Tweet from each organisation&#8217;s account, re-flow the Tweet so that I could make it run across the width of the screen in accordance with the font and font size I was using. Then finally display the font on the screen.<\/p>\n\n\n\n<p>I was going to do this in Python. <\/p>\n\n\n\n<p>I should point out up front that I rarely code and am not good at it. Furthermore, I&#8217;m terrible at remembering the syntax a language needs, even if I can understand roughly what&#8217;s going on.<\/p>\n\n\n\n<p>So you to do all this, I needed to &#8220;borrow&#8221; code from a variety of sources.<\/p>\n\n\n\n<p>I should also point out that I got this working in Python 2, because I couldn&#8217;t get things to work properly in Python 3. And for related reasons, I built the project with Raspbian Stretch rather than the recently released Buster.<\/p>\n\n\n\n<p>To replicate all of this, first <a href=\"http:\/\/downloads.raspberrypi.org\/raspbian\/images\/\">download an image of Raspbian Stretch<\/a>. I used the one dated &#8220;raspbian-2019-04-09&#8221;. Download the 1.1GB zip file.<\/p>\n\n\n\n<p>Then use a program like the free <a href=\"https:\/\/www.balena.io\/etcher\/\">balenaEtcher<\/a> to &#8220;flash&#8221; your zipped image onto a blank micro SD card. NB. If you do this on a PC, when it&#8217;s finished, you get lots of strange errors because PCs don&#8217;t like the formats of the card.<\/p>\n\n\n\n<p>Slot your microSD card into your Pi Zero. Plug in a monitor and keyboard, and go through the steps of connecting your Pi Zero to a WiFi network and running some updates. <\/p>\n\n\n\n<p>Open a command prompt on the Pi and run these updates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update\nsudo apt-get upgrade<\/code><\/pre>\n\n\n\n<p>These in turn get the list of updates available, and then upgrade those packages you have installed. This is just good practice for anyone using any kind of Raspberry Pi.<\/p>\n\n\n\n<p>To use the Inky WHAT, you need to turn on the Pi&#8217;s <em>I2C <\/em>interface. To do this, from a command prompt type:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo raspi-config<\/code><\/pre>\n\n\n\n<p>Mouse down to I2C and press enter. Turn it ON. And then Finish.<\/p>\n\n\n\n<p>Now shutdown your Pi and you can fit your Inky WHAT.<\/p>\n\n\n\n<p>When you&#8217;ve booted the Pi back up, you next need to install the software for the Inky WHAT. From a command prompt type:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl https:\/\/get.pimoroni.com\/inky | bash<\/code><\/pre>\n\n\n\n<p>Follow the prompts all the way through. Maybe get a cup of tea. Or two.<\/p>\n\n\n\n[Note that it may take quite a while to install everything. Although I ended up running things on a Pi Zero, I actually did all the development and setup using a Pi 3+. Then it was just a case of using the resulting microSD card in a Pi Zero.]\n\n\n\n<p>To check that everything is working, <a href=\"https:\/\/learn.pimoroni.com\/tutorial\/sandyj\/getting-started-with-inky-what\">you might want to follow the instructions on Pimoroni&#8217;s tutorial page<\/a>, and get their quote generator going. That way you can be sure that software library is properly installed and all is well with the device.<\/p>\n\n\n\n<p>Over on <a href=\"https:\/\/developer.twitter.com\/en\/apps\">Twitter&#8217;s development site<\/a>, you need to &#8220;Create an app&#8221; and give it a few details. They want to know a little about what you&#8217;re doing so fill in details as needed.  We also really only need &#8220;Read&#8221; permission.<\/p>\n\n\n\n<p>The important things that we need from here are details of the tokens. These need to kept secret, so don&#8217;t post them anywhere! Someone might use them to post <em>as<\/em> you if you&#8217;re not careful.<\/p>\n\n\n\n<p>There are four long alphanumeric strings you need:<\/p>\n\n\n\n<ul>\n<li>Consumer API key<\/li>\n\n\n\n<li>Consumer API secret key<\/li>\n\n\n\n<li>Access token<\/li>\n\n\n\n<li>Access secret token<\/li>\n<\/ul>\n\n\n\n<p>Save these somewhere safe, as you will need them in your Python code.<\/p>\n\n\n\n<p>You also need to import a Twitter library into your Pi:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install python-twitter<\/code><\/pre>\n\n\n\n<p>And here&#8217;s the main body of the code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import sys,twitter\nimport datetime\n\napi = twitter.Api()\n\n# Populate your twitter API details below, replacing\n# CONSUMER_KEY_HERE etc with your details from Twitter\n\nconsumer_key = 'CONSUMER_KEY_HERE'\nconsumer_secret = 'CONSUMER_SECRET_HERE'\naccess_token_key = 'ACCESS_KEY_HERE'\naccess_token_secret = 'ACCESS_SECRET_HERE'\n\napi = twitter.Api(\n    consumer_key=consumer_key,\n    consumer_secret=consumer_secret,\n    access_token_key=access_token_key,\n    access_token_secret=access_token_secret\n)\n\n# This is all setting up the Inky WHAT\n\nfrom inky import InkyWHAT\n\ninky_display = InkyWHAT(\"red\")\ninky_display.set_border(inky_display.WHITE)\n\nfrom PIL import Image, ImageFont, ImageDraw\n\nimg = Image.new(\"P\", (inky_display.WIDTH, inky_display.HEIGHT))\ndraw = ImageDraw.Draw(img)\n\n# I've used Arial as my font, and placed a copy in particular folder\n# Choose your own font, and locate the Truetype font in relevant folder\n\nfont = ImageFont.truetype(\"\/home\/pi\/Pimoroni\/inky\/news\/arial.ttf\", 18)\nfont_small = ImageFont.truetype(\"\/home\/pi\/Pimoroni\/inky\/news\/arial.ttf\", 14)\n\n# I've stolen this function from Sandy at Pimoroni. It's used\n# in their quote generator example.\n\n# This function will take a quote as a string, a width to fit\n# it into, and a font (one that's been loaded) and then reflow\n# that quote with newlines to fit into the space required.\n\n\ndef reflow_tweet(quote, width, font):\n    words = quote.split(\" \")\n    reflowed = ' '\n    line_length = 0\n\n    for i in range(len(words)):\n        word = words&#91;i] + \" \"\n        word_length = font.getsize(word)&#91;0]\n        line_length += word_length\n\n        if line_length &lt; width:\n            reflowed += word\n        else:\n            line_length = word_length\n            reflowed = reflowed&#91;:-1] + \"\\n \" + word\n\n    # reflowed = reflowed.rstrip() + '\"'\n\n    return reflowed\n\n# I used the code from here - https:\/\/zone13.io\/post\/python-code-latest-tweet\/ \n# to grab the most recent Tweet\n\n# I suspect that I could have created a function rather than repeat the following\n# code three times. But I haven't :-)\n\n# Note that I've hard coded in each Tweet.\n# Also, because I'm using a \"red\" version of Inky WHAT, my text is\n# either in red (for the source) or black (for the Tweet). \n# Change your colours as necessary.\n\n\n# BBC Breaking\n\ndef BBC_tweet(BBCBreaking):\n\tstatuses = api.GetUserTimeline(screen_name=\"BBCBreaking\")\n\treturn statuses&#91;0].text\n\t\nif __name__ == \"__main__\":\n\tBBC_latest_tweet = BBC_tweet(sys.argv&#91;1] if len(sys.argv) &gt; 1 else 0)\n\t\n\treflowed_BBC_latest_tweet = reflow_tweet(BBC_latest_tweet, inky_display.WIDTH, font)\n\t\t\n\tdraw.text((0, 0), \"BBC Breaking News\", inky_display.RED, font)\n\tdraw.text((0, 20), reflowed_BBC_latest_tweet, inky_display.BLACK, font)\n\t\n\n# New York Times\n\ndef nyt_tweet(nytimes):\n\tstatuses = api.GetUserTimeline(screen_name=\"nytimes\")\n\treturn statuses&#91;0].text\n\t\nif __name__ == \"__main__\":\n\tnyt_latest_tweet = nyt_tweet(sys.argv&#91;1] if len(sys.argv) &gt; 1 else 0)\n\t\n\treflowed_nyt_latest_tweet = reflow_tweet(nyt_latest_tweet, inky_display.WIDTH, font)\n\t\t\n\tdraw.text((0, 100), \"New York Times\", inky_display.RED, font)\n\tdraw.text((0, 120), reflowed_nyt_latest_tweet, inky_display.BLACK, font)\n\n\t\n# Reuters\n\ndef reuters_tweet(Reuters):\n\tstatuses = api.GetUserTimeline(screen_name=\"Reuters\")\n\treturn statuses&#91;0].text\n\t\nif __name__ == \"__main__\":\n\treuters_latest_tweet = reuters_tweet(sys.argv&#91;1] if len(sys.argv) &gt; 1 else 0)\n\t\n\treflowed_reuters_latest_tweet = reflow_tweet(reuters_latest_tweet, inky_display.WIDTH, font)\n\t\t\n\tdraw.text((0, 200), \"Reuters\", inky_display.RED, font)\n\tdraw.text((0, 220), reflowed_reuters_latest_tweet, inky_display.BLACK, font)\n\n\n# The following just inserts the last updated time\n# in the bottom right corner.\n\nnow = datetime.datetime.now()\ntweet_update = \"Updated: \" + now.strftime(\"%d-%m-%y %H:%M\")\ndraw.text((230,285), tweet_update, inky_display.RED, font_small)\n\n\n# At this point we finally show the information\n\ninky_display.set_image(img)\ninky_display.show()<\/code><\/pre>\n\n\n\n<p>The comments, delineated by &#8216;#&#8217; symbols explain where you need to make your own changes.<\/p>\n\n\n\n<p>Things to change include:<\/p>\n\n\n\n<ul>\n<li>Entering your Twitter keys<\/li>\n\n\n\n<li>Changing the Twitter accounts that you follow<\/li>\n\n\n\n<li>If you&#8217;re not using a red version of Inky WHAT, change references to &#8220;RED&#8221; to an appropriate colour<\/li>\n\n\n\n<li>Change the font to your own choice, and make sure there&#8217;s a copy of the font in the correct folder on your Pi.<\/li>\n<\/ul>\n\n\n\n<p>Name the files something appropriate like <em>twitter-news.py<\/em> (the .py suffix for Python).<\/p>\n\n\n\n<p>As I said, I have liberally borrowed from Sandy of Pimoroni&#8217;s sample code, in particular for the &#8220;reflow&#8221; function, and <a href=\"https:\/\/zone13.io\/post\/python-code-latest-tweet\/\">from here<\/a> for the general latest Tweet code.<\/p>\n\n\n\n<p>I almost certainly could have simplified this code, but I haven&#8217;t because I am a bad coder.<\/p>\n\n\n\n<p>To run this from a command prompt type <em>python twitter-news.py<\/em> and with any luck your display should change. Note that it can take quite a few seconds first time around. <\/p>\n\n\n\n<p>The final thing I wanted it to do was update the display every fifteen minutes. Linux has a useful <em>crontab<\/em> utility that will run programs in the background.<\/p>\n\n\n\n<p>First of all you want to change the permission of your <em>twitter-news.py<\/em> script to be executable. If you don&#8217;t do this, only you can run it. From a command prompt in the same folder as your script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod +x twitter-news.py<\/code><\/pre>\n\n\n\n[In case you didn&#8217;t already know, <em>ls<\/em> will show you what&#8217;s in a folder; <em>cd folder_name<\/em> will take you into a folder called <em>folder_name<\/em>; <em>cd ..<\/em> will take you up a folder level; <em>mkdir folder_name<\/em> will make a folder called <em>folder_name<\/em>]\n\n\n\n<p>Then we can use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>crontab -e<\/code><\/pre>\n\n\n\n<p>The first time you run this, you&#8217;ll be offered a choice of editors. Stick to nano.<\/p>\n\n\n\n<p>Then scroll to the bottom of the file and add the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>*\/15 * * * * python \/path\/to\/your\/folder\/twitter-news.py<\/code><\/pre>\n\n\n\n<p>Obviously, replace &#8220;\/path\/to\/your\/folder\/&#8221; withe actual path to where you&#8217;ve saved your Python script.<\/p>\n\n\n\n<p>Save and exit from Crontab. This will run the script every 15 minutes &#8211; in fact precisely on the quarter hours. And it will do it as soon as the Pi boots up. You won&#8217;t need a keyboard or display plugged in.<\/p>\n\n\n\n<p>And that should do it! <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Flaws\/Issues<\/h4>\n\n\n\n<p>One technical flaw is that I&#8217;ve hard-coded where the Tweets appear on the screen. But you can include carriage returns in Tweets, and they can run to 280 characters. So occasionally you get double-printing where a Tweet appears over the top of the following one. I guess the code could shorten Tweets to prevent this happening.<\/p>\n\n\n\n<p>More fundamentally, different news organisation use Twitter in different ways. So I&#8217;m not sure this quite works as a &#8220;Breaking News&#8221; display.<\/p>\n\n\n\n<p>As I said, I ended up using the BBC Breaking News feed, the New York Times feed, and main Reuters feed. But the BBC only updates their feed semi-regularly when something important is happening. You may go days without that feed updating. On the other hand, both the NY Times and Reuters update frequently, but the latest thing they Tweeted may not be the most important or leading story. They just rotate through stories that they want to promote &#8211; at least until a major truly breaking story emerges.<\/p>\n\n\n\n<p>For a good example of this, look at the Reuters Tweet in my photo above. I don&#8217;t think that truly constitutes breaking news, particularly in comparison with the other two headlines!<\/p>\n\n\n\n<p>Each of the Tweets tends to come with a URL that Twitter shortens. I guess I could have written some more code to chop the URL off, because it&#8217;s next to useless here. I can&#8217;t &#8220;click&#8221; through. But it doesn&#8217;t worry me too much.<\/p>\n\n\n\n<p>So my screen is arguably flawed in the sense it just gives a flavour of what the news organisations are reporting or have been reporting.  But during times of big breaking news stories, the three accounts are more likely to be aligned.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>This was quite a fun project, and it was surprisingly simple to get things displayed. <\/p>\n\n\n\n<p>I did run into problems getting this all to work under Buster &#8211; the new version of Raspbian. I think as much as anything because I wasn&#8217;t installing external libraries correctly for Python 3 which Buster seems to prioritise. But it&#8217;s possible that some of the display libraries haven&#8217;t been updated to work with Buster just yet.<\/p>\n\n\n\n<p>I ended up using a larger Inky WHAT having &#8220;broken&#8221; my smaller Inky PHAT screen. I&#8217;d originally built a version that used the smaller screen and cycled through the three Twitter sources, displaying each in turn. <\/p>\n\n\n\n<p>As it turned out, I&#8217;d actually only broken the micro SD card and not the screen at all. So now I need to think of something else to do with the smaller screen. I&#8217;m thinking of some kind of updating wearable badge connected to my phone. <\/p>\n\n\n\n<p>But that&#8217;s for another day.<\/p>\n\n\n\n<p><em>Note: The image above shows my Twitter News display between a Google Home Hub and my self-built Raspberry Pi based National Rail Dashboard. <a href=\"https:\/\/dev.adambowie.com\/2018\/12\/building-a-desktop-national-rail-dashboard\/\">For details on how to build that, click here.<\/a><\/em><\/p>\n\n\n\n<p><em>Update &#8211; August 2023: This ran fine for several years until finally Twitter was sold to Elon Musk and he basically killed the free tier of the Twitter API which this project utilised. My plan now is to re-work this using RSS feeds instead.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>See update at the bottom if you&#8217;ve come to this from Google. For some time now, I&#8217;d been meaning to create a little display that shows me the latest news. I&#8217;d had a Pimoroni Inky PHAT kicking around for a while, but it&#8217;d had broken. For complicated reasons, I ended up with one of Pimoroni&#8217;s [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":8874,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[997,996,995,994,921,122],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/posts\/8832"}],"collection":[{"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/comments?post=8832"}],"version-history":[{"count":5,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/posts\/8832\/revisions"}],"predecessor-version":[{"id":77824,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/posts\/8832\/revisions\/77824"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/media\/8874"}],"wp:attachment":[{"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/media?parent=8832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/categories?post=8832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.adambowie.com\/blog\/wp-json\/wp\/v2\/tags?post=8832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}