{"componentChunkName":"component---src-templates-page-js","path":"/notes/making-the-worlds-first-internet-enabled-fantasy-football-trophy-part-2-programming/","result":{"data":{"markdownRemark":{"html":"<img class=\"aligncenter size-medium wp-image-538\" alt=\"P1090435\" src=\"/img/uploads/2014/02/P1090435-800x460.jpg\"  />\n<p>In <a href=\"/notes/making-the-worlds-first-internet-enabled-fantasy-football-trophy-part-1-fabrication\">part 1</a>, I walked through the fabrication process of the Trophy of the Future. If you haven’t read that yet, go check it out! In this post, part 2, I’m going to discuss the technology behind the trophy.</p>\n<p>At the heart of it all is an <a href=\"http://arduino.cc/en/Main/ArduinoBoardYun?from=Main.ArduinoYUN\">Arduino Yún</a>, the first Arduino board to also include a Linux processor. The processor runs a <a href=\"https://github.com/sambrenner/future-trophy/blob/master/trophy.py\">Python script</a> that collects the names of the champions of my fantasy football league, the league’s standings and NFL news via the Yún’s built-in WiFi chip and scrolls across a 40×8 LED matrix. The LEDs are controlled on the software side by the <a href=\"http://parola.codeplex.com/\">Parola</a> library and on the hardware side by 5 8×8 LED modules, each with its own <a href=\"http://playground.arduino.cc/Main/MAX72XXHardware\">MAX7219</a> chip. When the Yún is plugged in, the Arduino processor asks the Linux processor to run the python script, which returns the next line for the LEDs to display. Once the line is finished scrolling, Arduino again asks for the next line, and this continues… forever!</p>\n<p>Here’s the Python:</p>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> sys <span class=\"hljs-keyword\">import</span> argv\n<span class=\"hljs-keyword\">import</span> os\n<span class=\"hljs-keyword\">import</span> feedparser\n<span class=\"hljs-keyword\">import</span> json\n<span class=\"hljs-keyword\">import</span> urllib\n<span class=\"hljs-keyword\">import</span> apikey\n\ntrophy_data_path = <span class=\"hljs-string\">'http://www.samjbrenner.com/projects/trophy/trophy.json'</span>\nnfl_news_path = <span class=\"hljs-string\">''</span>\nnfl_news_intro = <span class=\"hljs-string\">''</span>\nleague_api_path = <span class=\"hljs-string\">''</span>\nleague_standings_intro = <span class=\"hljs-string\">''</span>\nleague_api_collection_names = []\nnews_stories_max = <span class=\"hljs-number\">0</span>\nmessage_buffer_path = <span class=\"hljs-string\">'message_buffer.txt'</span>\noffline_data_path = <span class=\"hljs-string\">'offline_data.txt'</span>\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title\">get_next_message</span><span class=\"hljs-params\">()</span>:</span>\n  message_buffer = open(message_buffer_path, <span class=\"hljs-string\">'a+'</span>)\n  message_buffer.seek(<span class=\"hljs-number\">0</span>)\n\n  <span class=\"hljs-comment\"># if file is empty, refill buffer</span>\n  <span class=\"hljs-keyword\">if</span> message_buffer.readline() == <span class=\"hljs-string\">''</span>:\n    <span class=\"hljs-keyword\">try</span>:\n      get_trophy_data()\n\n      <span class=\"hljs-keyword\">with</span> open(offline_data_path) <span class=\"hljs-keyword\">as</span> offline_data:\n        message_buffer.writelines(<span class=\"hljs-string\">\"%s\"</span> % item <span class=\"hljs-keyword\">for</span> item <span class=\"hljs-keyword\">in</span> offline_data.readlines()[<span class=\"hljs-number\">1</span>:])\n\n      message_buffer.writelines(<span class=\"hljs-string\">\"%s\\n\"</span> % item <span class=\"hljs-keyword\">for</span> item <span class=\"hljs-keyword\">in</span> get_league_data())\n      message_buffer.writelines(<span class=\"hljs-string\">\"%s\\n\"</span> % item <span class=\"hljs-keyword\">for</span> item <span class=\"hljs-keyword\">in</span> get_nfl_news())\n\n    <span class=\"hljs-keyword\">except</span> IOError <span class=\"hljs-keyword\">as</span> e:\n      <span class=\"hljs-keyword\">with</span> open(offline_data_path) <span class=\"hljs-keyword\">as</span> offline_data:\n        message_buffer.writelines(<span class=\"hljs-string\">\"%s\"</span> % item <span class=\"hljs-keyword\">for</span> item <span class=\"hljs-keyword\">in</span> offline_data.readlines()[<span class=\"hljs-number\">1</span>:])\n\n  <span class=\"hljs-comment\"># get first line</span>\n  message_buffer.seek(<span class=\"hljs-number\">0</span>)\n  next_message = message_buffer.readline()\n\n  <span class=\"hljs-comment\"># replace file with all lines except for the first line</span>\n  remaining_lines = message_buffer.readlines()\n  message_buffer.seek(<span class=\"hljs-number\">0</span>)\n  message_buffer.truncate()\n  message_buffer.writelines(<span class=\"hljs-string\">\"%s\"</span> % item <span class=\"hljs-keyword\">for</span> item <span class=\"hljs-keyword\">in</span> remaining_lines)\n  message_buffer.close()\n\n  <span class=\"hljs-keyword\">print</span> next_message\n  <span class=\"hljs-keyword\">return</span> next_message\n\n<span class=\"hljs-comment\"># network functionality</span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title\">get_trophy_data</span><span class=\"hljs-params\">()</span>:</span>\n  <span class=\"hljs-keyword\">global</span> nfl_news_path, nfl_news_intro, news_stories_max, league_api_path, league_api_collection_names, league_standings_intro\n\n  json_response = urllib.urlopen(trophy_data_path)\n  data = json.loads(json_response.read())\n\n  nfl_news_path = data[<span class=\"hljs-string\">'nfl_news_path'</span>]\n  nfl_news_intro = data[<span class=\"hljs-string\">'nfl_news_intro'</span>]\n  news_stories_max = data[<span class=\"hljs-string\">'news_stories_max'</span>]\n  league_api_path = data[<span class=\"hljs-string\">'league_api_path'</span>].replace(<span class=\"hljs-string\">'API_KEY'</span>, apikey.value)\n  league_api_collection_names = data[<span class=\"hljs-string\">'league_api_collection_names'</span>]\n  league_standings_intro = data[<span class=\"hljs-string\">'league_standings_intro'</span>]\n\n  store_offline_data(data)\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title\">get_nfl_news</span><span class=\"hljs-params\">()</span>:</span>\n  nfl_feed = feedparser.parse(nfl_news_path)\n\n  titles = [nfl_news_intro]\n\n  <span class=\"hljs-keyword\">for</span> entry <span class=\"hljs-keyword\">in</span> nfl_feed.entries[:news_stories_max]:\n    titles.append(entry.title)\n\n  <span class=\"hljs-keyword\">return</span> titles\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title\">get_league_data</span><span class=\"hljs-params\">()</span>:</span>\n  json_response = urllib.urlopen(league_api_path)\n  data = json.loads(json_response.read())\n\n  standings = [league_standings_intro]\n\n  <span class=\"hljs-keyword\">for</span> collection_name <span class=\"hljs-keyword\">in</span> league_api_collection_names:\n    data = data[collection_name]\n\n  <span class=\"hljs-keyword\">for</span> player <span class=\"hljs-keyword\">in</span> sorted(data, key=<span class=\"hljs-keyword\">lambda</span> x:int(x[<span class=\"hljs-string\">'rank'</span>])):\n    standings.append(<span class=\"hljs-string\">\"%s: %s, %s (%s)\"</span> % (player[<span class=\"hljs-string\">'rank'</span>], player[<span class=\"hljs-string\">'name'</span>][<span class=\"hljs-string\">'text'</span>], player[<span class=\"hljs-string\">'record'</span>], player[<span class=\"hljs-string\">'streak'</span>]))\n\n  <span class=\"hljs-keyword\">return</span> standings\n\n<span class=\"hljs-comment\"># file i/o</span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def</span> <span class=\"hljs-title\">store_offline_data</span><span class=\"hljs-params\">(data)</span>:</span>\n  offline_data = open(offline_data_path, <span class=\"hljs-string\">'w'</span>)\n\n  offline_data.write(<span class=\"hljs-string\">\"%s\\n\"</span> % data[<span class=\"hljs-string\">'offline_warning'</span>])\n\n  <span class=\"hljs-keyword\">for</span> line <span class=\"hljs-keyword\">in</span> data[<span class=\"hljs-string\">'intro_message'</span>]:\n    offline_data.write(<span class=\"hljs-string\">\"%s\\n\"</span> % line)\n\n  <span class=\"hljs-keyword\">for</span> champion <span class=\"hljs-keyword\">in</span> data[<span class=\"hljs-string\">'champions'</span>]:\n    offline_data.write(<span class=\"hljs-string\">\"%s: %s, %s\\n\"</span> % (champion[<span class=\"hljs-string\">'year'</span>], champion[<span class=\"hljs-string\">'owner'</span>], champion[<span class=\"hljs-string\">'teamname'</span>]))\n\n  offline_data.close()\n\n<span class=\"hljs-comment\"># instructions</span>\n<span class=\"hljs-keyword\">if</span> len(argv) > <span class=\"hljs-number\">1</span>:\n  <span class=\"hljs-keyword\">if</span> argv[<span class=\"hljs-number\">1</span>] == <span class=\"hljs-string\">'nextmsg'</span>:\n    get_next_message()\n  <span class=\"hljs-keyword\">else</span>:\n    <span class=\"hljs-keyword\">print</span> <span class=\"hljs-string\">'Please supply a valid command.'</span>\n<span class=\"hljs-keyword\">else</span>:\n  <span class=\"hljs-keyword\">print</span> <span class=\"hljs-string\">'Please supply a command.'</span></code></pre>\n<p>And here’s the Arduino code:</p>\n<pre><code class=\"hljs language-cpp\"><span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include</span> <span class=\"hljs-meta-string\">&#x3C;MD_Parola.h></span></span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include</span> <span class=\"hljs-meta-string\">&#x3C;MD_MAX72xx.h></span></span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include</span> <span class=\"hljs-meta-string\">&#x3C;SPI.h></span></span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include</span> <span class=\"hljs-meta-string\">&#x3C;Process.h></span></span>\n\n<span class=\"hljs-comment\">//arduino pins</span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">define</span> MAX_DEVICES 5</span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">define</span> CLK_PIN 10</span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">define</span> DATA_PIN 8</span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">define</span> CS_PIN 9</span>\n\n<span class=\"hljs-comment\">//parola</span>\nMD_Parola scroller = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);\nMD_Parola::textEffect_t scrollEffect = MD_Parola::SCROLL_LEFT;\n<span class=\"hljs-keyword\">uint8_t</span> frameDelay = <span class=\"hljs-number\">100</span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">setup</span><span class=\"hljs-params\">()</span> </span>{\n  initScroller();\n  Bridge.begin();\n  scrollNextMessage();\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">loop</span><span class=\"hljs-params\">()</span> </span>{\n  <span class=\"hljs-keyword\">if</span> (scroller.displayAnimate()) {\n    scrollNextMessage();\n  }\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">initScroller</span><span class=\"hljs-params\">()</span> </span>{\n  scroller.begin();\n  scroller.displayClear();\n  scroller.displaySuspend(<span class=\"hljs-literal\">false</span>);\n\n  scroller.displayScroll(<span class=\"hljs-string\">\"loading...\"</span>, MD_Parola::LEFT, scrollEffect, frameDelay);\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">scrollNextMessage</span><span class=\"hljs-params\">()</span> </span>{\n  Process process;\n  String nextMessage = <span class=\"hljs-string\">\"\"</span>;\n\n  process.runShellCommand(<span class=\"hljs-string\">\"python /root/trophy/trophy.py nextmsg\"</span>);\n  <span class=\"hljs-keyword\">while</span>(process.running());\n\n  <span class=\"hljs-keyword\">while</span>(process.available()) {\n    nextMessage += (<span class=\"hljs-keyword\">char</span>)process.read();\n  }\n\n  <span class=\"hljs-keyword\">char</span> msg[nextMessage.length()];\n  nextMessage.toCharArray(msg, nextMessage.length() - <span class=\"hljs-number\">1</span>);\n\n  scroller.displayScroll(msg, MD_Parola::LEFT, scrollEffect, frameDelay);\n}</code></pre>\n<p>The reason I went with an Arduino over a similar board like the <a href=\"http://www.raspberrypi.org/\">Raspberry Pi</a> or <a href=\"http://beagleboard.org/\">BeagleBone</a> was mainly due to my familiarity with the Arduino interface. The Arduino community has been around for longer and has already worked through a lot of the uncertainties I was trying to eliminate in the project. Finding and testing out the Parola library made me certain that Arduino was the way to go.</p>\n<p>The most noteworthy thing about this project (for me, at least) is that it was my first time ever programming with Python. Python was the natural choice because it is supported out of the box on the Yún and because I’ve been looking for an excuse to learn it for some time now. I started working through “<a href=\"http://learnpythonthehardway.org/\">Learn Python the Hard Way</a>” and quickly had the bulk of my trophy code written. The code does a few things:</p>\n<ol>\n<li>First, it checks my server for a <a href=\"http://www.samjbrenner.com/projects/trophy/trophy.json\">configuration file</a> that contains the list of champions and URLs for the other APIs I’ll be calling and some instructions on what to do with them. This will give me a certain amount of control of the trophy even when it’s in someone else’s home.</li>\n<li>Next, it populates a message buffer text file. To do this, it accesses the APIs (one being an NFL news RSS feed and the other being my NFL.com fantasy league’s standings), formats the responses properly and saves them to the text file. One of the coolest technologies used in this process is <a href=\"http://www.kimonolabs.com/\">Kimono</a>, a totally-pain-free tool for building APIs out of scraped web data (necessary because NFL.com doesn’t provide their fantasy data in a developer-friendly format).</li>\n<li>Messages that are not as time-dependent, like the champions and the welcome messages, are also saved to a separate file to be used in case the Yún is unable to access the internet.</li>\n<li>Any subsequent time that the Arduino asks for a message, the Python program checks to see if there’s anything in the message buffer and if so, returns it to the Arduino. Once the message buffer is empty, the whole process starts again.</li>\n</ol>\n<p>This is enough for an alpha release (and also because the fantasy season ended a month ago and the champion wants his trophy now!) but there are a bunch of things I still want to add:</p>\n<ol>\n<li>Incorporate pauses between message cycles – for example, once every hour. I imagine the bright red scrolling LEDs could get annoying pretty quickly.</li>\n<li>A user-friendly way to configure the Arduino’s WiFi access. Since the trophy will change hands (and WiFi networks) every year, and most of those people will have had no experience using an Arduino, I need to come up with an easy way they can reconfigure the board to connect to their network.</li>\n<li>Remote updating. It should be easy to update the Python code remotely. I haven’t looked into reprogramming the Arduino remotely but I imagine that would be a more difficult task.</li>\n<li>I would love to play more with animation on the LED matrix. Scrolling text gets a little boring after a while! I’m thinking I can use an animation program like After Effects to produce a file where 1 pixel of animation equals one 1 LED, and then translate the pixel information to a matrix of 1s and 0s that the Arduino could send to the trophy.</li>\n</ol>\n<p>All of the code is on <a href=\"https://github.com/sambrenner/future-trophy/\">GitHub</a>, so go check it out!</p>","frontmatter":{"path":"/notes/making-the-worlds-first-internet-enabled-fantasy-football-trophy-part-2-programming/","title":"Making the world's first internet-enabled fantasy football trophy, part 2: Programming","date":"2014-02-01T16:48:24.000Z","categories":["Arduino","Materials","Fabrication","ITP","Materials and Building Processes","Project Write-Up","Python","Trophy of the Future"],"churl":null,"uses":null}}},"pageContext":{"post":true,"prev":{"url":"/notes/making-the-worlds-first-internet-enabled-fantasy-football-trophy-part-1-fabrication/","title":"Making the world's first internet-enabled fantasy football trophy, part 1: Fabrication"},"next":{"url":"/notes/using-pip-to-install-python-packages-on-the-arduino-yun/","title":"Using pip to install python packages on the Arduino Yún"}}}}