REST API on a Pi, Part 2: control your GPIO I/O ports over the internet

In Part 1 of this series, we built a simple REST API in JavaScript on our Raspberry Pi.
One of the most useful reasons for providing your Raspberry Pi with a REST API is to expose its input and output ports via the Internet for remote monitoring and control. This will allow you to control your RPi’s inputs and outputs from the browser on any smartphone or PC wherever you are in the world.
So now we will do just this, extending our REST API implementation from part 1 to read and display digital input ports.

The GPIO

A useful feature of the Raspberry Pi is the GPIO ports, made available through the header marked P1 on the PCB. These pins are a physical interface between the Pi and the outside world. Think of them as inputs you can connect to a sensor to send your RPi a digital on/off signal, such as a button press; or that the Pi can turn on or off to control a digital output – the simplest being an LED. 17 of the 26 pins of header P1 are GPIO pins; the others are power or ground pins.

A Note of GPIO Numbering

When programming the GPIO pins there are two different ways to refer to them: GPIO port numbering and physical pin numbering.
GPIO PORT NUMBERING
These are the GPIO ports as the CPU sees them.
PHYSICAL PIN NUMBERING
The other way to refer to the pins is by simply counting across and down from pin 1 at the top left of header P1 (nearest to the SD card). This is ‘physical numbering’. E.g.: GPIO port 23 is found on P1 header pin 16.

GPIO_Pi2 pinout

The Gertboard

You can implement this example directly on the P1 header pins of your Pi using but we will be using the Gertboard – an extended I/O interface board specially designed as a multi-purpose prototyping board for the Raspberry Pi. It features a range of additional I/Os, all driven off the RPi’s GPIO ports. For our purposes here, it has LEDs which usefully indicate the logic level of the ports set up as inputs.
IMG_3606
The Gertboard comes with a C library for accessing all its many features, but for the purposes of our JavaScript REST API we’ll simply be driving the GPIO with the pi-gpio Node module.

The pi-gpio Node Library Module 

We will be using the pi-gpio module, as described in this blog post
First, install the pi-gpio module

$ npm install pi-gpio

Modifications to our REST API source code from Part 1

We will need to make a number of changes to implement the GPIO interface.

MYAPI.JS: The Server(Pi)-side code

First, add the call to load the pi-gpio module

var wpi = require(' pi-gpio');

Recall we already defined an array for storing I/O values, which is set up for two GPIO ports. We will be using GPIO ports 23 and 25, the physical pins 16 and 22 :

var inputs = [{ pin: ‘16′, gpio: ‘23′, value: null },
{ pin: ‘22′, gpio: ‘25′, value: null }];.


We first need to initialise the GPIO and open the ports as inputs

var i;
for (i in inputs) {
 console.log('opening GPIO port ' + inputs[i].gpio + ' on pin ' + inputs[i].pin + ' as input');
 gpio.open(inputs[i].pin, "input", function (err) {
 if (err) {
 throw err;
 }
 });
}

We now need to add a timer loop to read each GPIO input and store the latest value in our inputs array. For this, we use the pi-gpio library function gpio.read.

setInterval( function () {
  // read GPIO input port
  gpio.read(inputs[0].pin, function (err, value) {
    if (err) {
      throw err;
    }
  console.log('read pin ' + inputs[0].pin + ' value = ' + value);
  // update the inputs object
  inputs[0].value = value.toString(); // store value as a string
});

Now when our API receives a request for one of the inputs, it will be sent the latest value read from the port.

MYCLIENT.JS: the client-side code

We can add a timer loop to call to our API server running on the Raspberry Pi to retrieve and display the input states at one-second intervals, passing a callback function to a setInterval library function call.

setInterval( function () {
for (i in ports) {
// call the API for each input port here
}
}, 1000); // setInterval to 1 second


Download the Source Code

You can download the full source code (client, server and html) for this part 2 example from github here https://github.com/fatkahawai/rpi-webapp-gpio
If you’ve installed the wiring-pi module as described above, then you can now just download the three source files into a new folder on your RPi using the git command, and you’re ready to go.

Running your Raspberry Pi Web App Server

Start up your web server on your RPi by invoking your application with node, from the folder you have saved these source files in. Note because you can only access the GPIO with root privileges, you’ll need to invoke node with sudo.

$ sudo node myapi.js
App Server running at port 3000

Now open your browser and navigate to your App Server hosted on the RPi.
If you’re in luck, this should appear in your browser window:
 Screen Shot 2015-06-01 at 2.47.08 pm

Accessing your API from beyond your LAN

One of the most useful reasons for providing your Raspberry Pi with a RESTful API is to expose its input and output ports beyond your LAN to the wider internet for remote monitoring and control.
To access your RPi’s new web API from the internet, you could open the port on your LAN router to allow inbound requests through, but this can expose your LAN to external attack, so its safer to use a service like weaved.com. You’ll need to sign up for an account, and install the weaved package on your RPi. Follow the instructions on their RPi page here 

What’s Next ?

Now you’ve got digital inputs implemented, you can use the same technique to expose the other features of the Gertboard – digital outputs and analog I/O.
I hope this exercise has been useful. If you have any feedback or spot any errors or omissions, feel free to leave a note in the comments section below.

 

23 comments

  1. Dmitry

    Great articles. Follow your blog.
    Have an errors in launchig myapi.js

    root@PiHome:/usr/local/auto/rpi-webapp-gpio# node myapi.js
    opening GPIO port 23 on pin 16 as input
    opening GPIO port 25 on pin 22 as input
    App Server is listening on port 3000
    Error when trying to open pin 22
    gpio-admin: could not flush data to /sys/class/gpio/export: Device or resource busy

    /usr/local/auto/rpi-webapp-gpio/myapi.js:39
    throw err;
    ^
    Error: Command failed: /bin/sh -c gpio-admin export 25
    gpio-admin: could not flush data to /sys/class/gpio/export: Device or resource busy

    at ChildProcess.exithandler (child_process.js:744:12)
    at ChildProcess.emit (events.js:110:17)
    at maybeClose (child_process.js:1008:16)
    at Socket. (child_process.js:1176:11)
    at Socket.emit (events.js:107:17)
    at Pipe.close (net.js:476:12)

    • bob@robert-drummond.com

      looks like the GPIO is still open from a previous session. just reboot your Pi, that will clear it.
      If you terminate the server application by hitting Ctl-C, that interrupt will be processed in myapi.js by the “process.on(SIGINT….” code, closing the GPIO ports and exiting gracefully, preventing you getting this “resource busy” error when next starting it.

      • Sam

        I’m getting the same problem – How do I fix it ?
        I cannot run it the next time as the export file is in that state is already present in that folder – so running ‘sudo node myapi.js’ the next time around is not helping. Is there a way to flush it, or resolve it ?
        Thanks

  2. Dmitry

    Looks like became a little bit better, but still no success. I start JS from root and from user with sudo, but the same result, no such file or directory.

    Here console.log

    sudo node /usr/local/auto/rpi-webapp-gpio/myapi.js
    opening GPIO port 23 on pin 16 as input
    opening GPIO port 25 on pin 22 as input
    App Server is listening on port 3000
    Error when trying to open pin 22
    gpio-admin: failed to change group ownership of /sys/devices/virtual/gpio/gpio25/direction: No such file or directory

    /usr/local/auto/rpi-webapp-gpio/myapi.js:39
    throw err;
    ^
    Error: Command failed: /bin/sh -c gpio-admin export 25
    gpio-admin: failed to change group ownership of /sys/devices/virtual/gpio/gpio25/direction: No such file or directory

    at ChildProcess.exithandler (child_process.js:744:12)
    at ChildProcess.emit (events.js:110:17)
    at maybeClose (child_process.js:1008:16)
    at Socket. (child_process.js:1176:11)
    at Socket.emit (events.js:107:17)
    at Pipe.close (net.js:476:12)

    That’s why it closes abnormally and don’t turn off GPIO

    Actually i don’t have this folder /sys/devices/virtual/gpio

    I’m using my Pi like a doorbell with python script and switch GPIO from Serial to Software Access Mode

    Taken from this guide – http://www.expertreviews.co.uk/accessories/gadgets/1401477/raspberry-pi-projects-make-your-doorbell-smart

    Next you need to convert the Pi’s GPIO (General-Purpose Input/Output) connection (the double line of bare pins near the SD card slot) from Serial mode to Software Access mode. To do this you need to edit a boot file. Type ‘sudo nano /boot/cmdline.txt’ and hit Enter. You’ll need to edit the current line:

    dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

    Maybe it’s the root of problem?

  3. Joseph Curtis

    I think you should update the blog, I was having a hard time wrapping my head around this one. I was trying to figure out where you put the port variable was, until I went to the source and it all made sense.

  4. Joseph Curtis

    I think you should update the blog, I was getting confused with the port variable, until I looked at the source.

  5. zarandok

    Hello, thank you very much for the tutorial. I’ve learned a lot while making it work on my Raspberry Pi.

    Imho the gpio inputs should be read in a loop, in the same manner as they are initialized:

    for (i in inputs) {
    gpio.read(inputs[i].pin, function (pin, err, value) {

    There is a catch, though: as the function will be called asynchronously, the value of i (and subsequently inputs[i].pin) may be already changed at the time of function call.
    The callback will need an additional ‘pin’ parameter to be able to correctly report the pin along with its value, something like:

    for (i in inputs) {
    gpio.read(inputs[i].pin, function (pin, err, value) {
    if (err) {
    throw err;
    }
    console.log(‘read pin ‘ + pin + ‘ value = ‘ + value);
    inputs[i].value = value;
    }.bind(undefined, inputs[i].pin));
    }

    which works for me.

  6. Rupesh Narayan

    Followed your blog, removed all the errors after reading all the comments. But still the problem persist with the new error:

    opening GPIO port 23 on pin 16 as input
    opening GPIO port 25 on pin 22 as input
    App Server is listening on port 3000

    /home/pi/myapp/myapi.js:49
    throw err;
    ^
    Error: ENOENT, open ‘/sys/class/gpio/gpio23/value’

    Help me resolve this problem…

  7. FRED

    Hi,

    thanks for this great tutorial! I am loving this.
    Any chance you could give a hint how to emebed gpio.write in the API ?

    Cheers

  8. Sandeep

    Nice tutorial, thank you very much for that tutorial….
    But now I have ultrasonic sensor connected to raspberry pi 3 and that sensor data, i have to show to the application (Android or web page)
    And which sensor data code is wrote in javascript
    So how i use their ports and send continusley my mesure distance to web page or apllication
    plzzz plzzz guide me this concept as soon possible
    Thank you.

  9. Emma

    Hi, Thanks for this cool tutorial!
    I created a REST API on my Raspberry Pi and made it accessible over remote.it (developer.weaved package). Accessing directly from remote.it works well.
    Now I would like to access this API over a VueJS web app. However I struggle making the requests over remote.it. Somehow it does not transfer me to the right URL. (Also I think the URL keeps changing – how do I deal with that?)

    Do you have experience with this service? I could really need some help. 🙂

    Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s