15 Feb 2014

Raspberry Pi control from mobile device or desktop web browser

In my disco ball hack project, I have been using my mobile phone browser to send commands to my Raspberry Pi. In this post, I will give you a quick overview on how to do the same thing and we will take a look at the code involved. I hooked up a LED and a button to my RPi so we can read the state of the button and toggle the LED. I'm using a self flashing RGB LED that I bought on Ebay, it looks cool :)

There are multiple ways to connect to the Raspberry Pi from another device. It could have been a usb-serial link, bluetooth or even raw socket programming. Also, you could have used other methods  to create the user interface by using native programming on the device :  Android or iPhone app, Visual Studio on the PC... The biggest advantage of using HTTP with a web server on the Pi is that it will work with all devices that have a browser. Also, you only need to know a bit of HTML and JavaScript, you don't need to learn all the other SDK and languages. I think that it's much easier to design a user interface using HTML and adding a bit of CSS for improving the look of the page than learning all the GUI toolkits out there. Using HTML5 and JavaScript is trending right now with all the web development. In Windows 8, apps can be created natively in HTML5 and JavaScript. Some of the new operating systems like Firefox OS and Tizen use HTML for everything. Anyway, let's get started with our application.

For the server sied software part, I'm using Python and the web framework Flask. There is a ton of these Python web frameworks (Django is the most well known) but Flask is great because it is really simple and does not need any configuration. For the HTML part, I'm using a bit of JQuery Mobile because it helps making a great looking page in a few line of HTML. There is also another reason to use JQuery: Ajax. (Asynchronous JavaScript and XML)

In a typical webpage without Ajax in which you want to send data to the server, you have to make an HTML form with a submit button. Once you press submit, a POST or GET request is done, your data is transmitted to the server and the page is completely reloaded with new data or you are redirected. With Ajax, you can easily send data to the web server without disrupting the page displayed. It is also possible to request JSON data from the server and modify the DOM (update only certain element of the page).

The simple circuit used

My circuit is an LED connected on P1 (WiringPi pin numbers) in series with a 470 ohm resistor. I also have a button between P0 and GND. I don't need a pull-up on the button because I'm using the internal pull-up of the Raspberry Pi GPIO. The circuit is even simpler like that.


Python code to access the button and LED

This code is fairly simple, I used the standard Raspberry Pi Python module to read/write to the GPIO. I made small utility functions so that later it is easier to read the code in the server Python file. The file name is Pins.py and I will use this as a module in the web server code.

import RPi.GPIO as GPIO

# BCM to WiringPi pin numbers
P0 = 17 # LED pin
P1 = 18 # Button pin

def Init():
    GPIO.setwarnings(False) # suppress GPIO used message
    GPIO.setmode(GPIO.BCM) # use BCM pin numbers
    GPIO.setup(P0, GPIO.OUT) # set LED pin as output
    # set button pin as input
    # also use internal pull-up so we don't need external resistor
    GPIO.setup(P1, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def LEDon():
    GPIO.output(P0, GPIO.HIGH)

def LEDoff():
    GPIO.output(P0, GPIO.LOW)

def SetLED(state):
    if state:
        LEDon()
    else:
        LEDoff()

# the "not" is used to reverse the state of the input
# when pull-up is used, 1 is returned when the button is not pressed
def ReadButton():
    if not GPIO.input(P1):
        return True
    else:
        return False

# if not used as a module (standalone), run this test program 
if __name__ == "__main__":
    Init()
    try:
        while(True):
            SetLED(ReadButton())
    except:
        #clean exit on CTRL-C
        GPIO.cleanup()
        quit()

Web server code

With Flask you can create a working web server in half a page of code. The Index function send the page to the web browser. Notice that the page is rendered only once, the rest is handled with Ajax. The function names that starts with the underscore are the Ajax request. They are called with the GetJSON function with JQuery (see the web page JavaScript in the next section). The _led function does not return JSON, but extract the state parameter from the GET url from the Ajax request to set the state of the LED. The _button function does not have parameter but instead return a JSONified text string corresponding to the button state. As a bonus, when the page is rendered first, I send the uptime of the Raspberry Pi. You can try to type "uptime" in a Unix terminal, you will see a status string displayed. It gives you the time, uptime, # of connected users and load average. I only wanted the uptime so I parsed it.

By the way, to get the Flask module on your Raspbian Python folder, enter the following commands:
sudo apt-get install python-pip
sudo pip install flask

The first command is to install pip (a tool for installing and managing Python packages).

from flask import Flask, render_template, request, jsonify
import Pins

app = Flask(__name__)

# return index page when IP address of RPi is typed in the browser
@app.route("/")
def Index():
    return render_template("index.html", uptime=GetUptime())

# ajax GET call this function to set led state
# depeding on the GET parameter sent
@app.route("/_led")
def _led():
    state = request.args.get('state')
    if state=="on":
        Pins.LEDon()
    else:
        Pins.LEDoff()
    return ""

# ajax GET call this function periodically to read button state
# the state is sent back as json data
@app.route("/_button")
def _button():
    if Pins.ReadButton():
        state = "pressed"
    else:
        state = "not pressed"
    return jsonify(buttonState=state)

def GetUptime():
    # get uptime from the linux terminal command
    from subprocess import check_output
    output = check_output(["uptime"])
    # return only uptime info
    uptime = output[output.find("up"):output.find("user")-5]
    return uptime
    
# run the webserver on standard port 80, requires sudo
if __name__ == "__main__":
    Pins.Init()
    app.run(host='0.0.0.0', port=80, debug=True)

Web page HTML code

In the web page, I import everything needed to get JQuery Mobile working. In the script section, I have two JavaScript functions : the first one detect a change on the slider-switch and send the state of this switch with an Ajax GET to the webserver, the second one is called once each 500 ms to get the button state. The rest of the HTML is the minimal structure of the page.

<!doctype html>
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.js"></script>
</head>

<style>
h3, h4 {text-align: center;}
span {font-weight: bold;}
</style>

<script type=text/javascript>
    $(
    // When the LED button is pressed (change)
    // do an ajax request to server to change LED state
    function() 
    {
        $('#flip-1').change(function() 
        {
        $.getJSON('/_led', {state: $('#flip-1').val()});
        });     
    }
    );

    $(
    // periodically (500ms) do an ajax request to get the button state
    // modify the span tag to reflect the state (pressed or not)
    // the state text comes from the JSON string returned by the server
    function button() 
    {
        $.getJSON('/_button', function(data)
            {
                $("#buttonState").text(data.buttonState);
                setTimeout(function(){button();},500);
            });
    }
    );
    

</script>
<!-- Simple JQuery Mobile page that display the button state on the breadoard -->
<!-- You can also change the LED state with the slider switch -->
<!-- The Raspberry Pi uptime is displayed in the footer (Jinja2 expands the template tag) -->

<div data-role="page" data-theme="b">
  <div data-role="header">
    <div><h3>Raspberry Pi Web Control</h3></div>
  </div>

  <div data-role="content">
    <form>
    <p>The button is <span id="buttonState"></span></p>
    <br>
    <select name="flip-1" id="flip-1" data-role="slider" style="float: left;">
        <option value="off">LED off</option>
        <option value="on">LED on</option>
    </select>
    </form>
  </div>
 <div data-role="footer">
    <div><h4>This Raspberry Pi has been {{uptime}}</h4></div>
  </div>
</div>

In action : demo video on Youtube





Here is a demo of the server running and being controlled by my mobile phone. On my main computer, I connect to the Raspberry Pi with SSH. I then start the python application. If you look closely on the computer screen around 0:15, you will see a lot of requests being made to the server. This is due to the 500ms timer sending the Ajax request to read the status of the button.



I really hope that this code can help you in your projects. When I started doing electronics stuff in 2007, I was dreaming about doing something like this. At that time, we did not have smart phones and low cost Linux single board computer. Things changed a lot since!