{"id":1494,"date":"2013-03-17T22:53:02","date_gmt":"2013-03-17T22:53:02","guid":{"rendered":"http:\/\/davstott.me.uk\/?p=1494"},"modified":"2013-05-30T09:43:04","modified_gmt":"2013-05-30T09:43:04","slug":"raspberry-pi-controlling-gpio-from-the-web","status":"publish","type":"post","link":"https:\/\/davstott.me.uk\/index.php\/2013\/03\/17\/raspberry-pi-controlling-gpio-from-the-web\/","title":{"rendered":"Raspberry Pi &#8211; Controlling GPIO from the Web"},"content":{"rendered":"<h1>Controlling a Raspberry Pi&#8217;s GPIO over the network<\/h1>\n<div id=\"attachment_1497\" style=\"width: 160px\" class=\"wp-caption alignright\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1497\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/01\/non-uber-gui1-150x150.jpg\" alt=\"My web user interface controlling LEDs. Bottle of Octomore is optional, but recommended.\" width=\"150\" height=\"150\" class=\"size-thumbnail wp-image-1497\" \/><p id=\"caption-attachment-1497\" class=\"wp-caption-text\">My web user interface controlling LEDs. Bottle of Octomore is optional, but recommended.<\/p><\/div>\n<p>The first step in playing with a Raspberry Pi&#8217;s GPIO interface is to turn an LED on and off on command, the Hello World of digital electronics.  As fun as that is, it would be more fun to do from my mobile phone using my home wireless network, this article runs through the software that I&#8217;ve used to do just that. I haven&#8217;t said that these techniques allow real-time web control of GPIO pins because they do not, but I get a response time of about 100ms using wireless ethernet with my Pi which does the job for me.<\/p>\n<p>I&#8217;m going to be using Python, as it&#8217;s a very popular language on the Raspberry Pi, a bit of HTML and Javascript to provide the user interface and some linuxy goodness to glue it all together. There are many other ways of doing this, but this one is mine. I&#8217;m glossing over most of the hardware issues, so please be careful not to blow up your Pi. <\/p>\n<p><!--more--><\/p>\n<p>The steps can be roughly broken down into:<\/p>\n<ol>\n<li>Hardware and environment set up<\/li>\n<li>Python and GPIO<\/li>\n<li>Python, FastCGI and a web server<\/li>\n<li>HTML and Javascript<\/li>\n<\/ol>\n<h2>Hardware and environment set up<\/h2>\n<p>This article intends to focus on the software side of things, so I&#8217;m going to assume that:<\/p>\n<ul>\n<li>you&#8217;ve got a fully functioning Raspberry Pi running a reasonably recent build of Raspbian Linux<\/li>\n<li>that is connected to your home network through wired or wireless<\/li>\n<li>that your home network has internet access through a router that uses NAT<\/li>\n<li>that you&#8217;re comfortable using your Pi&#8217;s command line interface<\/li>\n<\/ul>\n<p>With those out of the way, you will also need an LED and a resistor (I recommend a 330 ohm resister, but any value between 220 ohms and 3k3 ohms will do for this exercise) and some way of wiring those up to the Pi&#8217;s GPIO pins. One of the best ways is to use a breadboard and <a href=\"https:\/\/www.adafruit.com\/products\/914\">Adafruit&#8217;s Pi Cobbler<\/a>, but you can also just twist the wires together with insulating tape.\n<\/p>\n<p>Update 19th May 2013: After a conversation with Thomas, it&#8217;s worth noting that you need to be careful not to introduce Windows style line endings if copying and pasting text into your Raspberry Pi. Python itself isn&#8217;t bothered by line endings, but the Linux shell interpreter is and it can often fail to find Python if the #!\/usr\/bin\/python line has a Carriage Return as well as a Line Feed on it. If something isn&#8217;t quite working as you&#8217;d expect, it&#8217;s worth a quick check with the Linux &#8216;file&#8217; utility to see that&#8217;s the issue before spending time troubleshooting:<\/p>\n<pre>\r\nThis is what 'file' says for a file with Windows line endings \r\npi@raspberrypi \/tmp $ file foo.py\r\nfoo.py: Python script, ASCII text executable, with CRLF line terminators\r\nI've already installed a tool called dos2unix using sudo apt-get install dos2unix\r\npi@raspberrypi \/tmp $ dos2unix foo.py\r\ndos2unix: converting file foo.py to Unix format ...\r\nCheck it again, this time it's UNIX line endings and it's reported like this:\r\npi@raspberrypi \/tmp $ file foo.py\r\nfoo.py: Python script, ASCII text executable\r\npi@raspberrypi \/tmp $ \r\n<\/pre>\n<\/p>\n<p>Connect the LED and resistor in series between the Pi&#8217;s ground pin and one of its GPIO pins, I&#8217;ve used 18.<\/p>\n<div id=\"attachment_1552\" style=\"width: 308px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1552\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/03\/Pi-GPIO_bb-298x300.png\" alt=\"GPIO Wiring\" width=\"298\" height=\"300\" class=\"size-medium wp-image-1552\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/03\/Pi-GPIO_bb-298x300.png 298w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/03\/Pi-GPIO_bb-150x150.png 150w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/03\/Pi-GPIO_bb-624x627.png 624w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/03\/Pi-GPIO_bb.png 999w\" sizes=\"auto, (max-width: 298px) 100vw, 298px\" \/><p id=\"caption-attachment-1552\" class=\"wp-caption-text\">GPIO Wiring<\/p><\/div>\n<h2>Python and GPIO<\/h2>\n<p>A version of the RPi.GPIO python library is bundled with Raspbian Linux, so we&#8217;ll just use that for now to keep things simple. The following Python code will configure GPIO pin 18 as an output and then turn it on. This code has to run as root, so you need to save it to a file and then run it with:<\/p>\n<pre>\r\nsudo python myFile.py\r\n<\/pre>\n<p><noscript><\/p>\n<pre>\r\n#!\/usr\/bin\/python\r\nimport RPi.GPIO as G # reference the GPIO library\r\nG.setmode(G.BCM)     # use the 'BCM' numbering scheme for the pins\r\nG.setup(18, G.OUT)   # Set pin 18 as an Output\r\nG.output(18, True)   # Turn it on\r\nraw_input('Press return to exit')\r\nG.cleanup()          # Tidy up after ourselves so we don't generate warnings next time we run this\r\n<\/pre>\n<p><\/noscript><br \/>\n<script src=\"https:\/\/gist.github.com\/davstott\/5183828.js?file=myFile.py\"><\/script>\n<\/p>\n<p>To take this to its next level, we can allow for some user input by reading something from the terminal:<br \/>\n<noscript><\/p>\n<pre>\r\n#!\/usr\/bin\/python\r\nimport RPi.GPIO as G     # reference the GPIO library\r\nG.setmode(G.BCM)         # use the 'BCM' numbering scheme for the pins\r\nG.setup(18, G.OUT)       # Set pin 18 as an Output\r\nwhile (True):            # keep going around this loop until we're told to quit\r\n  key = raw_input(\"Enter 'w' for On, 's' for Off and any other key to quit. You'll need to press enter after each character: \")\r\n  if key == \"w\": \r\n    G.output(18, True)   # Turn it on\r\n  elif key == \"s\":\r\n    G.output(18, False)  # Turn it off\r\n  else:\r\n    break                # leave our loop\r\nG.cleanup()              # Tidy up after ourselves so we don't generate warnings next time we run this\r\n<\/pre>\n<p><\/noscript><br \/>\n<script src=\"https:\/\/gist.github.com\/davstott\/5183828.js?file=myFileWithUser.py\"><\/script>\n<\/p>\n<h2>Python, FastCGI and a web server<\/h2>\n<p>This is great, we&#8217;ve now got a small program running on the Pi that waits for a human to command it to turn the LED on and off, and then it does as its told, but we can take the next step of hooking it up to a web browser. To do this, we need to turn the Raspberry Pi into a web server and then create a small website on it.<\/p>\n<p>One of the great features of Raspbian Linux is that it&#8217;s got a really good repository of software that easy to install, we&#8217;ll be calling on that repository to install the <a href=\"http:\/\/www.lighttpd.net\/\">lighttpd Web Server<\/a> and the <a href=\"https:\/\/pypi.python.org\/pypi\/flup\">Flup Python library<\/a> to help us talk to that Web Server using <a href=\"http:\/\/redmine.lighttpd.net\/projects\/lighttpd\/wiki\/Docs_ModFastCGI\">FastCGI<\/a>.<\/p>\n<p>Install the web server and start it running using:<\/p>\n<pre>\r\nsudo apt-get install lighttpd\r\nsudo service lighttpd start\r\n<\/pre>\n<\/p>\n<p>By default, lighttpd runs with a single website that lives in the directory \/var\/www. We can set up our own website in there by creating a new file \/var\/www\/index.html and putting some HTML in it:<br \/>\n<noscript><\/p>\n<pre>\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n  \t&lt;title&gt;Hello from the Pi&lt;\/title&gt;\r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;h1&gt;Hello world from the Raspberry Pi&lt;\/h1&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p><\/noscript><br \/>\n<script src=\"https:\/\/gist.github.com\/davstott\/5183828.js?file=index.html\"><\/script><\/p>\n<p>Now when we point a web browser at the Raspberry Pi&#8217;s IP address, such as http:\/\/192.168.1.104\/ (substituting your own IP address into that URL), you should get the hello world message appearing<\/p>\n<p>To hook up our Python to the web server, we need to install a library that helps with the FastCGI protocol. FastCGI is slightly harder to configure than CGI, but it leaves the python script running in memory all of the time, whereas CGI has to start up a new Python interpreter for every request which is slow. <\/p>\n<pre>\r\nsudo apt-get -no-install-recommended install python-flup\r\n<\/pre>\n<p>and then change our python script to take its input from Flup instead from raw_input() and so that it runs as the root user.  Save the following script as \/var\/www\/doStuff.py and then chmod it to 755 so that the webserver can execute it.<\/p>\n<p><noscript><\/p>\n<pre>\r\n#!\/usr\/bin\/pythonRoot\r\n# bring in the libraries\r\nimport RPi.GPIO as G     \r\nfrom flup.server.fcgi import WSGIServer \r\nimport sys, urlparse\r\n\r\n# set up our GPIO pins\r\nG.setmode(G.BCM)\r\nG.setup(18, G.OUT)\r\n\r\n# all of our code now lives within the app() function which is called for each http request we receive\r\ndef app(environ, start_response):\r\n  # start our http response \r\n  start_response(\"200 OK\", [(\"Content-Type\", \"text\/html\")])\r\n  # look for inputs on the URL\r\n  i = urlparse.parse_qs(environ[\"QUERY_STRING\"])\r\n  yield ('&nbsp;') # flup expects a string to be returned from this function\r\n  # if there's a url variable named 'q'\r\n  if \"q\" in i:\r\n    if i[\"q\"][0] == \"w\": \r\n      G.output(18, True)   # Turn it on\r\n    elif i[\"q\"][0] == \"s\":\r\n      G.output(18, False)  # Turn it off\r\n\r\n#by default, Flup works out how to bind to the web server for us, so just call it with our app() function and let it get on with it\r\nWSGIServer(app).run()\r\n<\/pre>\n<p><\/noscript><br \/>\n<script src=\"https:\/\/gist.github.com\/davstott\/5183828.js?file=doStuff.py\"><\/script>\n<\/p>\n<p>By default, the \/usr\/bin\/python executable runs as the user that calls it. That&#8217;s likely to be either the &#8216;pi&#8217; user if you&#8217;re interactively using it, or the &#8216;www-data&#8217; user if it&#8217;s being run by the web server.  An easy to configure way of breaching this security is to user linux&#8217;s setuid feature. This is a potentially dangerous technique, so needs a bit of careful thought, but it is easy to set up.<\/p>\n<p>We find out which version of python is being used by default, then make a copy of it named \/usr\/bin\/pythonRoot and then make it run setuid root:<\/p>\n<pre>\r\n  pi@raspberrypi \/var\/www $ ls -l \/usr\/bin\/python\r\n  lrwxrwxrwx 1 root root 9 Jun  6  2012 \/usr\/bin\/python -> python2.7\r\n  pi@raspberrypi \/var\/www $ sudo cp \/usr\/bin\/python2.7 \/usr\/bin\/pythonRoot\r\n  pi@raspberrypi \/var\/www $ sudo chmod u+s \/usr\/bin\/pythonRoot\r\n  pi@raspberrypi \/var\/www $ ls -l \/usr\/bin\/pythonRoot\r\n  -rwsr-xr-x 1 root root 2674528 Mar 17 18:16 \/usr\/bin\/pythonRoot\r\n<\/pre>\n<\/p>\n<p>The final step is to configure our web server with its FastCGI module and to tell it where to find our Python script. Do this by editing \/etc\/lighttpd\/lighttpd.conf:<\/p>\n<pre>\r\nAdd a line to the server.modules = () list at the top of the file:\r\n  \"mod_fastcgi\",\r\n\r\nThen add the following block of code to the end of the file:\r\n fastcgi.server = (\r\n   \".py\" => (\r\n     \"python-fcgi\" => (\r\n       \"socket\" => \"\/tmp\/fastcgi.python.socket\",\r\n       \"bin-path\" => \"\/var\/www\/doStuff.py\",\r\n       \"check-local\" => \"disable\",\r\n       \"max-procs\" => 1)\r\n    )\r\n )\r\n\r\nNow restart your web server, \r\nsudo service lighttpd restart\r\n<\/pre>\n<\/p>\n<p>If all has gone perfectly first time (and what ever does?), then you should now be able to turn your LED on and off by calling URLs like this: http:\/\/192.168.1.104\/doStuff.py?q=w and http:\/\/192.168.1.104\/doStuff.py?q=s (again, swapping in your Pi&#8217;s IP address)<\/p>\n<h2>HTML and Javascript<\/h2>\n<p>Manually entering those URLs ourselves is all well and good, but who doesn&#8217;t like a nice button to push to make something happen? For that, we need to add two buttons and some javascript to our \/var\/www\/index.html. I&#8217;m also being lazy and using the <a href=\"http:\/\/prototypejs.org\/\">Prototype.js<\/a> javascript library to handle the AJAX requests.<\/p>\n<p><noscript><\/p>\n<pre>\r\n &lt;head&gt;\r\n  \t&lt;title&gt;Hello from the Pi&lt;\/title&gt;\r\n  \t&lt;script src=\"\/\/ajax.googleapis.com\/ajax\/libs\/prototype\/1.7.1.0\/prototype.js\"&gt;&lt;\/script&gt; \r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;h1&gt;Hello world from the Raspberry Pi&lt;\/h1&gt;\r\n    &lt;form&gt;\r\n    \t&lt;input type=\"button\" value=\"On\" onclick=\"go('w')\" style=\"font-size:200%;\"&gt;&lt;br \/&gt;\r\n    \t&lt;input type=\"button\" value=\"Off\" onclick=\"go('s')\" style=\"font-size:200%;\"&gt;\r\n    &lt;\/form&gt;\r\n    &lt;script type=\"text\/javascript\"&gt;\r\n      function go(qry) {\r\n\t    new Ajax.Request('doStuff.py?q=' + qry, \r\n\t    \t{method: 'GET'}\r\n        );\r\n      }\r\n    &lt;\/script&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p><\/noscript><br \/>\n<script src=\"https:\/\/gist.github.com\/davstott\/5183828.js?file=indexWithJs.html\"><\/script><\/p>\n<p>So now when you point your browser at http:\/\/192.168.1.104\/, the LED should turn on and off when you push the appropriate button!<\/p>\n<h2>Outstanding issues and ways of doing this better<\/h2>\n<p>As always, my code is up on <a href=\"https:\/\/github.com\/davstott\/piTank\">Github<\/a>. The most recent version of my code has the most features in, but you can see each feature being implemented by looking at the changes for each <a href=\"https:\/\/github.com\/davstott\/piTank\/commits\/master\">commit<\/a><\/p>\n<p>The RPi.GPIO python library needs to run as the root user, which opens up all kinds of security hazards for accidents or foul play, one way of dealing with this is to seperate the user input code from the doing stuff code. I did this myself using two Python scripts, or you could use <a href=\"https:\/\/github.com\/quick2wire\/quick2wire-python-api\">Quick2Wire&#8217;s Python API<\/a>.  <\/p>\n<p>Another note on security, it&#8217;s always important to not trust any user inputs by checking that they&#8217;re what you&#8217;re expecting, which is why I mentioned a NAT router at the start. NAT means that you have to go out of your way to allow internet connections directly to your Raspberry Pi, which protects it against casual abuse. Running that script as root means that we&#8217;re trusting the web server and our helper libraries to not have their own security vulnerabilities.  I&#8217;ve not mentioned local security because anybody with a terminal session to your Pi as the &#8216;pi&#8217; user has got full access to it by default and anybody with physical access can do anything they please. <\/p>\n<p>I&#8217;ve used FastCGI because it&#8217;s fairly concise to explain, but there are newer and better ways of connecting server scripts to the web these days, such as WSGI and Websockets. If you can assume an internet connection for both your web browser and the Raspberry Pi then you could use a service like <a href=\"http:\/\/pusher.com\/\">Pusher<\/a> which makes it all very easy.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Controlling a Raspberry Pi&#8217;s GPIO over the network The first step in playing with a Raspberry Pi&#8217;s GPIO interface is to turn an LED on and off on command, the Hello World of digital electronics. As fun as that is, it would be more fun to do from my mobile phone using my home wireless [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":["post-1494","post","type-post","status-publish","format-standard","hentry","category-tech"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1494","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/comments?post=1494"}],"version-history":[{"count":30,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1494\/revisions"}],"predecessor-version":[{"id":1675,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1494\/revisions\/1675"}],"wp:attachment":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/media?parent=1494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/categories?post=1494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/tags?post=1494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}