collage

Direct Raspberry Pi IO with Apache and WiringPi

So last time, we demonstrated how to control your Pi IO with an intermediate database, using either our CuPID Controls UI and interface or a barebones installation with IO setpoints. We like this arrangement, for many reasons, such as error-handling, safety interlocks, datalogging, race conditions, and many other bonus features. It’s what we use for all of our applications.

We have come to realize, however, that there are advantages and elegance to an even simpler approach. Specifically, if you want the fastest possible response and minimal infrastructure, you may want to manipulate the Raspberry Pi IO directly. You may not want to rely on an intermediate database and daemon script to enact your commands.  For each application there is a best (or better) approach.

For this reason, we decided to find an effective way to bypass the typical problem of manipulating IO from a web interface: scripts run by anybody other than root, www-data for example, cannot execute the typical API, RPi.GPIO, to enable or disable GPIO outputs. This is currently possible using specialty web services, such as WebIOPi, but we would like to do so on a standard web server, such as Apache. This offers the possibility of doing so over not just your local network, but from anywhere you wish, as it is over a universal web interface, using javascript and html.

WiringPi offers this possibility, using the IO access from /sys/class/GPIO, as documented here. We don’t do anything special here except set up a wsgi method to execute these commands and call it using some javascript/jQuery. These are detailed below.

 The Requirements

  • Apache with mod_wsgi enabled
  • WiringPi. Installation is covered here
  • wsgi script alias directed to your wsgi script, something like this in your site configuration file:
 WSGIScriptAlias /wsgigpioactions /usr/lib/iicontrollibs/wsgi/wsgigpioactions.wsgi
  • A wsgi script, at location above, outlined below
  • An html document sourcing jQuery, so we can use the ajax call

WiringPI

We won’t bore you with stuff better covered elsewhere, like all of the features of WiringPi, or how to install it. The main feature we exploit is listed here:

  • wiringPiSetupSys (void) ;

This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program. Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be aware of the differences between Rev 1 and Rev 2 boards.

The key feature, in bold, is that we can manipulate GPIO without root access. This is key, as our scripts executed from a web page via wsgi will be run as www-data.

Another nice feature of the package is that it comes with a handy command-line utility, gpio, that allows us to do our manipulation quite easily. So the code below will set up GPIO 18 for non-root access, and turn it on and then off:

gpio export 18 out
gpio -g write 18 1
gpio -g write 18 0

It doesn’t really get much easier than that. The export command automatically uses BCM numbering, and the ‘-g’ will let our gpio write command know to also use this scheme.

Although there is a python wrapper for this utility, we call it directly from our python scripts with a couple simple functions. More on that later.

The WSGI script

The server-side script is the special sauce. It is what will take our web page command and read or write on GPIO using WiringPi with the basic commands listed above. The mechanics of the wsgi call are explained here and here, but the gist is that with some leading headers and a return of data as requested, we can pass data to a wsgi script in json format, execute as we see fit in python, and return the data in a dictionary to do as we see fit in our web page. Take a look here for the complete wsgi script. We pass an object to the script in json defining an action, and act on our action as we see fit. Here, for example, is our action on an output toggle:

if d['action'] == 'wptoggleGPIOvalue':
    try:
        BCMpin = int(d['BCMpin'])
    except KeyError:
        data['message'] = 'No pin sent with command'
    else:
        from subprocess import check_output, call

        output = int(check_output(['gpio','-g','read','18']))
        # call(['gpio','export',BCMpin,'output'])
        if output == 0:
            call(['gpio','-g','write',BCMpin,'1'])
        else:
            call(['gpio','-g','write',BCMpin,'0'])

This is really basic stuff. The command assumes the gpio has already been exported as writable by /sys/class/,  reads the state, and writes the inverse. Piece of cake. We’ll add in some error-handling and export py-fu, but these are the nuts and bolts.

Polling data from the web page using jQuery

Using some basic ajax, we can execute calls to our wsgi script to get our data. The basic function is below:

function wsgiwpgpioactions (commands,callback) {
    // Get the data
    commands=commands || {action:'testaction'}
    callback = callback || logdone;
    $.ajax({
        url: "/wsgigpioactions",
        type: "post",
        datatype:"json",
        data: commands,
        success: function(response){
            callback(response)
        }
    });
}

We just need to pass it an action to carry out and a callback to execute on the data. It will execute asynchronously, execute the command we have issued, and return whatever data we have requested. Linking this to buttons, we can set up the jQuery action on a keypress:

$("testbutton").click(function)(){
    var command = {action:'wptoggleGPIOvalue',GPIOid:18}
    wsgigpioactions(command)
}

So in the above function, if we create a button with id of ‘testbutton’ and click it, it will execute a toggle of GPIO 18 as listed in the wsgi script above. Easy enough. If we want to get a little more complicated with it, we can apply the same key behavior to multiple button classes with different gpio IDs. The function below uses a helper function to extract a GPIO id from the DOM based on where the click came from. This way we don’t have to write  a button behavior for each:

$('.canpress').click(function(){
        var clickid = this.id
        var result = getGPIOfromid(clickid)
        if (result.mode == 'mode'){
            var action = 'wptoggleGPIOmode'
        }
        else if (result.mode == 'value') {
            var action = 'wptoggleGPIOvalue'
        }
        var command = {action:action,GPIOid:result.GPIOid}

        if (runqueries) {
//            alert('running command:')
//            alert('action: ' + action + ' , GPIOnum: ' + result.GPIOnum)
            wsgiwpgpioactions(command,updatestatusdata)
        }
        else {
            alert('queries not activated for command:')
            alert('action: ' + action + ' , GPIOnum: ' + result.GPIOnum)
        }
    });

Our helper function:

function getGPIOfromid(clickid) {
    if (clickid.search('mode') > 0){
            var mode = 'mode';
            // Chop up string
            // starts with GPIO
            // double-digit number
            if (clickid.search('mode') == 6) {
                var GPIOnum = clickid.slice(4,6);
                var GPIOid = clickid.slice(0,6);
            }
            // single digit number
            else {
                var GPIOnum = clickid.slice(4,5);
                var GPIOid  =clickid.slice(0,5);
            }
        }
        else if (clickid.search('value') > 0) {
            var mode = 'value';
            // Chop up string
            // starts with GPIO
            // double-digit number
            if (clickid.search('value') == 6) {
                var GPIOnum = clickid.slice(4,6);
                var GPIOid = clickid.slice(0,6);
            }
            // single digit number
            else {
                var GPIOnum = clickid.slice(4,5);
                var GPIOid  =clickid.slice(0,5);
            }
        }
    return {GPIOnum:GPIOnum, mode:mode, GPIOid:GPIOid}
}

 Putting it all together

Now, as we did before, we’ll set up a physical GPIO interface page, so we can monitor status and punch some buttons. First of all, we’ll need to get our gpio status regularly to update our buttons. So we wrap a text parser around the gpio readall function to return a dictionary array of current gpio statuses:

def getgpiostatus():

    from subprocess import check_output

    gpiolist=[]
    alloutput = check_output(['gpio','readall'])
    lines = alloutput.split('\n')[3:18]
    for line in lines:
        BCM1 = line[4:6].strip()
        wpi1 = line[10:12].strip()
        name1 = line[15:22].strip()
        mode1 = line[25:30].strip()
        val1 = line[32:34].strip()
        phys1 = line[36:39].strip()

        phys2 = line[42:44].strip()
        val2 = line[46:48].strip()
        mode2 = line[50:55].strip()
        name2 = line[57:65].strip()
        wpi2 = line[68:70].strip()
        BCM2 = line[74:76].strip()

        if BCM1 and BCM1 != '--':
            gpiolist.append({'BCM': BCM1, 'wpi': wpi1, 'name': name1, 'mode': mode1, 'value': val1, 'phys': phys1})
        if BCM2 and BCM2 != '--':
            gpiolist.append({'BCM': BCM2, 'wpi': wpi2, 'name': name2, 'mode': mode2, 'value': val2, 'phys': phys2})

    return gpiolist

We put that into our general-purpose library, pilib, and set up our wsgi script to call it (you can easily grab the function from our git blob and put it into your wsgi script if you don’t want pilib)

if d['action'] == 'wpgetgpiostatus':
    from pilib import getgpiostatus
    data = getgpiostatus()

And define a function in our web page to call the wsgi and return the data to the web page:

//// Update all GPIO data
function updategpioData() { wsgiwpgpioactions({action:'wpgetgpiostatus'},updatestatusdata)
}

This function runs our our ajax function wsgiwpgpioactions, with a callback of updatestatusdata, which will run once the ajax function is done. This function  takes the data as returned and inserts it into the DOM using some jQuery;

function updatestatusdata(statusdata){
    var text = '';
    $.each(statusdata, function(index,gpiodict){

        var value = gpiodict.value;
        var mode = gpiodict.mode;

        var valueelements =  $('.' + 'GPIO' + statusdata[index].BCM + 'value');
        valueelements.removeClass('on')
        valueelements.removeClass('off')
        if (value == '1'){
            text = 'On';
            valueelements.addClass('on')
        }
        else if (value == '0'){
            text = 'Off';
            valueelements.addClass('off')
        }
        else {
            text = 'Err';
        }
        valueelements.html(text);
        var modeelements = $('.' + 'GPIO' + statusdata[index].BCM + 'mode');

        modeelements.removeClass('input')
        modeelements.removeClass('output')
        if (mode == 'IN'){
            text = 'In';
            modeelements.addClass('input')
        }
        else if (mode == 'OUT'){
            text = 'Out';
            modeelements.addClass('output')
        }
        else if (mode == 'ALT0'){
            text = 'I2C';
            modeelements.addClass('I2C')
        }
        else {
            text = 'Err';
        }
        modeelements.html(text)
    })
}

So all we have to do is label our html elements properly with classes, and our update function will populate them nicely. For example:

<tr>
   <td>11</td>
   <td align="center"><div class="GPIO17value lightindicator canpress" id="GPIO17value">X</div></td>
   <td>&nbsp;</td>
   <td align="center"><div class="GPIO18value lightindicator canpress" id="GPIO18value">X</div></td>
   <td>12</td>
</tr>

You’ll notice above we added and removed ‘on’ and ‘off’ classes, and a few others, so that the buttons and elements look proper when they change values:

.lightindicator.on, .lightindicator.input {
        background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #77ff77), color-stop(1, #00cc00) );
        background:-moz-linear-gradient( center top, #77ff77 5%, #00cc00 100% );
        filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#77ff77', endColorstr='#00cc00');
        background-color:#ededed;
    }
    .lightindicator.off {
        background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dddddd), color-stop(1, #777777) );
        background:-moz-linear-gradient( center top, #dddddd 5%, #777777 100% );
        filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dddddd', endColorstr='#777777');
        background-color:#ededed;
    }

Demo

So let’s do something a bit more interesting with the framework that demonstrates we can do some real-time operation. We stripped down our Pi GPIO page EVEN FURTHER to contain only four buttons. We define them as 18, 23, 24, 25, in this version. We create four super-big buttons on our page, like so:

Our basic four GPIO button screen. Press a button, turn on an output.
Our basic four GPIO button screen. Press a button, turn on an output.

Next, we hook up some china special laser diodes on a board with some mosfets. We use one of our power output boards because it nicely breaks out our GPIO and power on the CuPID COM1 or COM2 bus. This is really nothing fancier than directly connecting our GPIO to the transistor gates. We’re just using what we have available here … and using our COM power lets us put our lights wherever we want (or as far as we care to stretch ethernet cable. Anyhow, here are a few pictures:

Our test setup. A CuPID and a few laser diodes, hooked up to four GPIO.
Our test setup. A CuPID and a few laser diodes, hooked up to four GPIO.
A close-up of our output setup. We're using a CuPID output board as a breakout for the GPIO on ethernet and to grab 5V and ground.
A close-up of our output setup. We’re using a CuPID output board as a breakout for the GPIO on ethernet and to grab 5V and ground. We machined up a little holder for our laser diodes.
The backside of our output breadboard. Four NPN 2N7000 mosfets, four 47ohm current limiting resistors, and four 10k pull downs on our outputs.
The backside of our output breadboard. Four NPN 2N7000 mosfets, four 47ohm current limiting resistors, and four 10k pull downs on our outputs.

And finally, let’s show it in motion. Beware, poor quality video.

2 thoughts on “Direct Raspberry Pi IO with Apache and WiringPi”

  1. Hi !
    Actually, I’m using RPi.GPIO to drive my GPIOs through daemon scripts.
    I would like to use it to read GPIO.
    Are your scripts (using wiring pi) compatible with my config ?
    I’m afraid of conflicts between the 2 modules.

    1. Hi Fabien,

      I don’t think these conflict, but even in the case they do, you can easily refactor your existing scripts to use wiringpi. I use pigpio for everything now as I found a nasty memory leak in RPi.GPIO that caused sporadic crashing.

      Cheers,
      C

Leave a Reply

Your email address will not be published. Required fields are marked *