Friday, 11 March 2016

Raspberry Pi - I finally get it.

I've never really been a fan of the Raspberry Pi. Mostly because I don't understand it. Not that I don't understand Linux (but I don't) nor that I don't understand the R/Pi's favoured programming language Python (I do, but I don't like it). It's just that - for a long time - I never really understood what the point of the Raspberry Pi was.

About 95% of the projects online that involve a Pi are using vastly over-rated hardware to do relatively simple stuff. Read a temperature sensor and report it over the interwebs? An 8-bit micro and a wifi module would do that just as well. The Pi is massively over-spec for most of these kinds of projects.

For anything a little more adventurous, it's really not quite beefy enough. Real-time object tracking would be a great idea - but at 700Mhz it's not quite got the grunt-power to do actual real-time processing, without a noticeable (and slightly annoying) lag.

Whichever type of project it's used for, it always seemed to be an odd fit. Too beefy for simple stuff. And not beefy enough for complex stuff. Which meant that most projects for the Raspberry Pi are simply ports from other platforms, just for the sake of it, rather than because the Pi was best suited to the job.



And for that reason, I'd not really bothered with the Raspberry Pi.
I got one and played with it. I got a camera and the model B+ (and the Pi2 when it came out). I even ported some simply blinky LED stuff, just to see what it could do. But it was always just a fancy gadget with no real purpose. A few of the other nerds got one and made it do fancy stuff - but in all honesty, not really much more than you can do with an 8-bit micro and some clever coding skills!

But in recent weeks, we've been using the Raspberry Pi quite a bit. It's still a horrible little thing to program for, using Python. But it's actually been quite useful for one major use: creating a web interface for accessing hardware.

Until recently, we've been using either wifi or bluetooth to talk to our hardware - the ESP8266 and the BTLE4 modules are cheap enough (at just a couple of quid) to throw onto an 8-bit micro to create a gateway for an app to talk to it. But as easy as this is to do, it almost always results in us creating some kind of app to talk to the bluetooth/wifi module.



So, at last, we've found a real purpose for a Raspberry Pi.
Sticking a web server (Apache) on the Pi and installing php means we can create and serve web pages that any device - iOS, Android, Windows, Linux - anything with wifi and a web browser can access. And because the Pi has an accessible i/o port, we can route requests from the web server to turn pins on and off. And, as any good nerd knows, if you can flash an LED, you can make your hardware do just about anything!

So with our Pi set up as a wifi access point, we can simply connect, open a browser and use HTML and javascript to turn things on and off. While people have been doing this for years, it's the combination of the camera module, accessing the photos it creates over wifi and bundling the whole lot up into a web-based app using websockets (to reduce latency) that suddenly means we're looking at the Raspberry Pi in a whole new light!

Just using a web server and calling webpages and ajax functions is ok. But it's relatively slow (in computing terms). Using web sockets means we're able to get our response times down to almost as low as a regular "ping" round-trip; sub-20-milliseconds is quite feasible.

So here's how we set up our socket server:



The script websocket.py runs on the Raspberry Pi, responding to incoming web socket connections
and messages. At the minute, we're using it as a simple relay server, but it proves the idea quite nicely. Here's the web browser sending and receiving messages from the Raspberry Pi over web sockets.


The web socket server (Python) script:

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket

class WebSocketHandler(tornado.websocket.WebSocketHandler):
      def open(self):
            print "new connection"
            self.write_message("connected")

      def on_close(self):
            print "connection closed"

      def on_message(self, message):
            print "message received {}".format(message)
            self.write_message("message received: "+message)

      def check_origin(self, origin):
            return True

if __name__ == "__main__":
      tornado.options.parse_command_line()
      app = tornado.web.Application( handlers=[(r"/", WebSocketHandler)] )

      httpServer = tornado.httpserver.HTTPServer(app)
      httpServer.listen(1975)
      print "Listening on port 1975"
      tornado.ioloop.IOLoop.instance().start()

      # this is a blocking thread - nothing more to do here


And the HTML to talk to the web socket server:

<html>

<head>
   <script src="jq.mobile/jquery-2.2.1.min.js" >
</script>
</head>

<body>
<br/><br/>

<textarea id="log"></textarea><br/>
<input type="text" id="txtSend" ><input type="button" value=" send " onclick="sendData();" />

<script>
   var socket;
   function logIt(s){
      var t=$('#log').val();
      t=t+"\n"+s;
      $('#log').val(t);
   }

   function sendData(){
      var t=$('#txtSend').val();
      try {
         socket.send(t);
      } catch(exception){
         logIt("Error sending "+exception);
      }
   }

   if(window.WebSocket){
      socket = new WebSocket("ws://192.168.42.1:1975");
      socket.onopen = function(){
         logIt("Socket status "+socket.readystate+" (open)");
      }
      socket.onmessage = function(msg){
         logIt("Received ["+msg.data+"]");
      }
      socket.onclose = function(){
         logIt("That's all folks!");
      }

   }else{
      alert("You can't use this, bog off");
   }
</script>

</body>
</html>


This combination of web server to deliver fancy app-like performance, plus websockets to give serial-like communications speeds, means we can control our hardware with great looking interfaces without having to deploy and install native apps for each device type. Finally, a task for which the Raspberry Pi is perfectly suited!