Using Node.js and the Express Framework to Access the Colibri VF61 GPIO via Web

Monday, April 25, 2016

ColibriThis article is meant for anyone who wishes to quickly start developing an embedded Linux web application with access to the hardware GPIO, so that the user can control and/or monitor a system remotely. The application developed here, is based on the Colibri VF61 CoM (Computer on Module) by Toradex + the Iris Carrier Board, shown in the image 1, and a PCB with LEDs and switches. The main goals are to walk the reader through the process of developing Node code for accessing the GPIO via filesystem, building a simple yet user-friendly UI, and using the Express framework for hosting a minimal webserver that communicates with the client-side application by AJAX calls. To help understand the client-side application, previous basic knowledge of HTML+CSS, jQuery and AJAX is recommended, even though the reader can skip this part and focus on the server-side application, if the reader only wants to have an overview about Node. All the codes developed in this guide can be copied from the following GitHub repositories: nodeGPIOdemo, WebNodeGPIOdemo and WebNodeMultiGPIOdemo.

Access to the Colibri VF61 GPIO using Node.js and AJAX

Image 1: Toradex VF61 + Iris Carrier Board

Node.js is a server-side runtime environment used for developing server-side applications written in JavaScript. Since Linux can use the filesystem as a means to access hardware features and Node has a built-in module to handle filesystem operations, an application can be developed in order to access the GPIO pins provided by the system. Node also provides modules such as HTTP that can be used to build a webserver, but the Express framework makes it easier to be done, and that's the reason we will use it.

JavaScript is a programming language interpreted by the client, which means it is processed by the user's browser. Combined with HTML and CSS, JavaScript makes possible the creation of web pages with which the user can interact, also known as responsive design. Since programming in JavaScript can be time-consuming, there is a cross-browser library called jQuery that simplifies tasks such as DOM manipulation, event handling and the use of the AJAX method, which is a set of techniques used to communicate asynchronously with the server and, therefore, dynamically change the contents of a web page.

Regarding the Colibri VF61 module, there are up to 99 pins that can be configured as GPIO. Since we are using it with the Iris Carrier Board, there are 8 GPIO pins that can be used out-of-the-box - of which 6 will be used in this tutorial - and another 18 pins that would need further configuration to be used as GPIO. The pins 13 to 18 of the Iris were chosen to be interfaced to 3 active-low switches (pins 13-15) and 3 active-high LEDs (pins 16-18), but you can access the Colibri VF61 datasheet and the Iris Carrier Board technical datasheet in case you want to configure more pins as GPIO. If you have any doubts regarding the pin numbers used, you can quickly refer to the table 1 and, in more details, to the datasheet links provided above and to a table that links the pin names of the VF61 SoC and the ones used by Linux.

Table 1: Correspondence between Iris, SODIMM connector, Vybrid and Kernel pin names.

Iris
(x16)
SODIMM
(x1)
Vybrid Kernel Iris
(x16)
SODIMM
(x1)
Vybrid Kernel
13 98 ptc1 46 16 101 ptc2 47
14 133 ptd9 88 17 97 ptc5 50
15 103 ptc3 48 18 85 ptc8 53

Configuration of the environment
For this article, the pre-built image Colibri_VF_LinuxConsoleImageV2.5 was used, and you can download it here. Detailed instructions on how to flash your module can be found here and, alternatively, you could also build your own customized image using the OpenEmbedded framework.

You should then plug your board to your network. If you are following this guide and don't want to plug your board to the LAN you're on, check this guide on how to setup networking for embedded Linux application development. Otherwise, set your board IP address to static, using connman, so you can easily access it through ssh and/or the browser.

Then you can access your board through SSH.

leonardo@leonardo:/tmp/leo$ ssh root@192.168.0.180
root@colibri-vf:~#


The first thing needed to write the codes is to install Node.js on your board. This can be done by executing the following commands to install and verify that it is working:

root@colibri-vf:~# opkg update
root@colibri-vf:~# opkg install nodejs
root@colibri-vf:~# node
> process.exit()
root@colibri-vf:~#js


Although everything you need to run node code is to execute node yourcode.js in the terminal, there is an utility called nodemon that watches for changes in the source codes and restarts the server automatically, sparing the bother that is to ctrl+c then node file.js, i.e. restart manually, while developing an application. The Express framework will be required later, so let's install it. We will also install the body-parser middleware in order to parse JSON encoded strings. The procedure below installs nodemon, Express and body-parser. It is previously needed to install tar and npm (node package manager), and it may take a while.

root@colibri-vf:~# opkg install tar
root@colibri-vf:~# curl -L https://www.npmjs.com/install.sh | sh
root@colibri-vf:~# npm install express
root@colibri-vf:~# npm install body-parser
root@colibri-vf:~# npm install -g nodemon


GPIO basics and accessing from the terminal
After logging into the board, the first step to use one specific GPIO pin is to configure it. By default, after a reset all the GPIO pins are configured to have the output buffer and internal pull up/down disabled, meaning that the pins are in high-Z state.

To use the pins through sysfs they first need to be exported. This is done by writing the pin number to the file /sys/class/gpio/export.

Export and unexport are files to which you can write the pin numbers that you want to use or set as free to the Kernel, respectively. Notice that if you try to export a pin already being used you will get an error. The gpiochipN are the GPIO controllers and each of them manages 32 pins - if you want to know which controller is responsible for which pin, it can be calculated using the following formula:
                                                                                                                        pin_number = gpioN[pin] + 32*N

For instance, the pin 88 that we will use corresponds to the pin 24 of the controller 2, or gpio2[24]. There is an easier way to find the pin correspondence regarding the VF61, by checking the columns GPIO Pad and GPIO Port of the module datasheet.

Let's export one of the GPIO pins that we are going to use, and then check the /sys/class/gpio directory:

root@colibri-vf:~# echo 46 > /sys/class/gpio/export 
root@colibri-vf:~# ls /sys/class/gpio/
export       gpio46       gpiochip128  gpiochip64   unexport
gpiochip0    gpiochip32   gpiochip96


Now, as you can see, the exported GPIO has a folder, so let's check its contents:

root@colibri-vf:~# ls -l /sys/class/gpio/gpio46/
-rw-r--r--    1 root     root          4096 Jan 19 18:40 active_low
lrwxrwxrwx    1 root     root             0 Jan 19 18:40 device -> ../../../4004a000.gpio
-rw-r--r--    1 root     root          4096 Jan 19 18:40 direction
-rw-r--r--    1 root     root          4096 Jan 19 18:40 edge
drwxr-xr-x    2 root     root             0 Jan 19 18:40 power
lrwxrwxrwx    1 root     root             0 Jan 19 18:40 subsystem -> ../../../../../../../class/gpio
-rw-r--r--    1 root     root          4096 Jan 19 18:39 uevent
-rw-r--r--    1 root     root          4096 Jan 19 18:40 value


Since everything here is done by reading and writing operations, to configure the pin it is just needed to write in or out in the direction file - the pins are configured as input when exported after a system reset. The value file is used to set or get the pin value, and it returns 0/1 even if you want to read an output current state; as for setting the pin, any numeric value different from 0 will be interpreted as 1.

Sometimes GPIO systems have diverging meanings regarding their active state and the logic value read by the system - for instance, all the switches used in this article are active low, meaning that if you read a zero from the pin, it means that the pushbutton is pressed, i.e. active. Sometimes it can be confusing while coding, so there is the option to invert the logic state read, by writing 1 to the active_low file: then, whenever you read/write 1 to value it means the pin logic state is set to 0.

There are some pins that have the edge file available, meaning they can be configured as interrupt generating pins. The file status can be none, rising, falling or both, concerning the edges that can generate the interrupts. The process of using this feature is to poll the file until one interrupt is generated. For now this topic will not be detailed, but more information on how to use interrupts with node can be found here.

Using Node.js to handle GPIO
Let's start developing our code. Assuming you will be starting from scratch, create a file in your home or project folder, name it as server.js and open it. I chose to use the Eclipse IDE to access and edit the files from the host machine using the Remote System Explorer, but you may prefer another IDE or just a text editor. Now that you have the file open, the first thing is to export the File System module. It is also a good idea to assign the pin numbers and constant values, for clarity and ease of maintenance.

/* Modules */
var fs = require('fs');
 
/* VF61 GPIO pins */
const   LED1 = '47', //you need to add the other pins from the table 1
 
/* Constants */
const HIGH = 1, LOW = 0;


Then we create functions for configuring, reading and writing a GPIO pin. Since this is an introductory guide, we won't worry about error handling, though it is highly recommended you add this functionality if you need a robust code, and even consider creating a module so that you can reuse this code easily. Fill in the required parts.

function cfGPIO(pin, direction){
/*---------- export pin if not exported and configure the pin direction --------*/
        fs.access('/sys/class/gpio/gpio' + pin, fs.F_OK, function(err){
                if(err){
                        console.log('exporting GPIO' + pin);
                        fs.writeFileSync('/sys/class/gpio/export', pin);
                }
                console.log('Configuring GPIO' + pin + ' as ' + direction);
                fs.writeFileSync('/sys/class/gpio/gpio' + pin + '/direction', direction);
        });
}
 
function rdGPIO(pin){ /*you need to read the value file and return it*/ }
 
function wrGPIO(pin, value){ /*you need to write value to the file*/ }


The rdGPIO and wrGPIO functions are straightforward: they only encapsulate the functions that read and write to the system files and put their arguments together, so the main code portion will look cleaner. For the cfGPIO function, first check if the file isn't already exported, and do it if required, and then configure the pin direction. The checking portion is essential, since it will show an error if you export an already exported pin. Using sync functions should also be noticed: since Node is asynchronous, so if the use of synchronous functions is not enforced, then there is a risk that some read or write operation may occur before/during the pin configuration, which will also result in an error.

The next thing needed is to call the configuration function first for each pin - we will do it in the simplest way, but you could use a loop-based approach if you have many pins to use. We are now ready to focus into the application functionality, so let's do something basic as a starting point: polling the switches and copying them to the respective LEDs. The native function setInterval calls our application function periodically: the first argument is the function to be called and the second the time between calls, in milliseconds, which in our application could be interpreted as a soft debouncing. Note the use of setImmediate - it is needed because of the asynchronous behavior of Node, to ensure that the pins will be configured before the code start polling them.

//setImmediate(function cfgOurPins(){
        cfGPIO(LED1, 'out'); //you should complete it for the other GPIO pins
        setInterval(copySwToLed, 50);
//});


Now let's reboot before running our application, so we can check that the pins are being correctly exported and configured and the rest is running smoothly. Check the results displayed and test the hardware, also.

root@colibri-vf:~# reboot
root@colibri-vf:~# nodemon server.js


Some ideas for you to try/implement by yourself:

  • Press the pushbuttons to see the system working
  • Try changing the debouncing time to a higher value, e.g. 1000, and notice the delay between holding the switch pressed and the LED switching
  • You could also implement in the cfGPIO function the optional parameter to configure the active_low file, so there would be no need to invert the switches status in our code before passing it to the LEDs
  • If you want a robust code, add some error handling portion to the code
  • Consider creating a module for the GPIO functions and procedures

Building a webserver and a web UI to access the board
From now on we will start using the debug module, which installs with Express, to display log messages to the terminal. To use the debug, the variable DEBUG should be set to the name used in the code, or * to display all debug messages. Also, the GitHub corresponding to this section example is here.

DEBUG=myserver nodemon server.js --ignore index.js


Now it is time to implement the Express webserver. First, we will require the modules related to Express and debug, then add to the code as constants the IP address of our board and a port to listen. Everything required for us to display a web page for our application is to point a directory to serve the static files, such as CSS, JavaScript, images, etc. and listen to a specified IP address and port. The constant __dirname refers to the current directory. The changed/added code portions will be like this:

/* server.js */
/* Modules */
var fs = require('fs');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var debug = require('debug')('myserver');
 
/* Constants */
const HIGH = 1, LOW = 0, IP_ADDR = '192.168.0.180', PORT_ADDR = 3000;
 
//Using Express to create a server
app.use(express.static(__dirname));
app.listen(PORT_ADDR, IP_ADDR, function () {
    debug('Express server listening at http://%s:%s %s', host, port, family);
});


Now the client-side code needs to be written. The name index.html will be used because the server looks for this filename by default, although it may be changed. We will also create a file named client.js to write the JavaScript/jQuery code portion. Above the first version of index.html is displayed and it is important to notice the input and label elements, because it will be the button used to control the LEDs later. Notice that in the head section, the jQuery library and the client.js file are already included for late usage. For a practical approach on web languages, methods, libraries and more, a good start is this website.

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
      <meta charset="UTF-8">
      <title>Colibri VF61 node.js webserver</title>
        <!-- Add jQuery library -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <script type="text/javascript" src="client.js"></script>
</head>

<body>
    <h1>Access to the VF61 GPIO using Node.js and AJAX</h1>
       <form>
              <input type="checkbox" class="btn" id="btn1">
              <label for="btn1" id="btn1l" style="color:red">OFF</label>
        </form>
</body>
</html>


If you open a browser and navigate to the address that the server is configured to listen, you should get the response from image 2. Notice that you can check the box but nothing happens when you click it.

Access to the VF61 GPIO using Node.js and AJAX
Image 2: First web application design

The client-side JavaScript code client.js that add responsiveness to our page and also communicates with the server is implemented below. First of all, jQuery gets the click event for all elements with btn class. Then a conditional clause checks if the button was already pressed or not and changes the label accordingly, setting the pressed button id and value properties of the btn_status object that will be sent to the server. At last, jQuery is used to send an AJAX HTTP POST to the server, containing the previously mentioned btn_status object, encoded as a JSON string.

/* client.js */
$(function(){
        $(".btn").click(function clickHandling(){
                var btn_status = {id:"", val:""};
 
                if(this.checked){
                        $(this).siblings().html("ON").css("color","green");
                        btn_status.id = $(this).attr("id");
                        btn_status.val = "on";
                }
 
                else{
                        $(this).siblings().html("OFF").css("color","red");
                        btn_status.id = $(this).attr("id");
                        btn_status.val = "off";
                }
 
                $.post("/gpio", btn_status, function (data, status){ 
                        if(status == "success"){
                                console.log(data);
                        }
                },"json");
        });
});


Before effectively using it, there is also the need to add the server-side code (server.js) to receive HTTP POST and make decisions based on the data received. It is also essential to remove the polling of switches, or both parts will conflict and the desired behavior won't be achieved. First thing needed is to set the body-parser - it must be done before setting the route. Then the route is set and its name must match the one used in the client application, but you can choose whichever name you want - additionally, you can have more than one route. Then the function that handles incoming POST requests is set: it gets the values received by the server and changes the LEDs states accordingly, and then sends a response to the server - it could, for instance, contain data to be checked by the client, to assure that all went as planned.

/* server.js */
app.use(bodyParser.urlencoded({ //must come before routing
        extended: true
}));
app.route('/gpio')
.post(function (req, res) {
        var serverResponse = {status:''};
        var btn = req.body.id, val = req.body.val;
 
        if(val == 'on'){
                // call wrGPIO to write 1 to them
                serverResponse.status = 'LEDs turned on.';
                res.send(serverResponse);
        }
        else{
            // call wrGPIO to write 0 to them
            serverResponse.status = 'LEDs turned off.';
            res.send(serverResponse);
        }
});


Now the web app is responsive and you can control the LEDs from it! This is the simplest example of how to control an output GPIO through the web, using AJAX to talk to the Node server. Here are some things you can try by yourself:

  • Add a status property to the server response and check it on the client - could be something like "success" and "fail" messages combined to the implementation of error handling functionality to the server-side GPIO functions
  • Use the setInterval function on the client-side code to poll a switch state. Send periodic POST requests and answer the current switch status. If you are unable to do that, don't worry, later on in this article we will develop an application to poll the switches states and update the UI with its values

Styling and using for multiple LEDs/switches
Although this code may be useful in the real world, the UI isn't much friendly - take for instance the result in image 3 of using our application on a smartphone, emulated by the device mode provided by the Chrome terminal. So we will just adapt our code to use the Bootstrap framework, mostly CSS-based and mobile-first. Using other frameworks such as PhoneGap, that create apps for mobile devices from a web application just like ours, and/or styling the UI by yourself are other possible choices that you may check.

Application emulated on a smartphone screen

Image 3: Previously coded application emulated on a smartphone sized screen

The first thing we are going to do is to adapt our codes to use the Bootstrap framework, starting by the HTML file. Although it may seem that many changes were made, remember that most of it won't affect the way things work, but the way things look. In the head section, we will add a meta tag to provide mobile-first support and the links to the bootstrap CSS and JavaScript libraries. Then in the body section, we will first add a container div, that provides a responsive and fixed-width div in which our page will be constrained. Inside it, we will add a row div, since Bootstrap works with a grid system - inside this div, a column div will be added with a width of 4, out of 12 provided by Bootstrap.

The major difference concerning our application is that we stopped using a checkbox/label input and started to use a button instead: its configuration is in the class attribute, where btn and btn-block are Bootstrap classes, and true_btn will be used by us to differ switches from LEDs. Also, it makes the process of changing its style/label much easier, because there is no need to find the corresponding label tag to a checkbox tag.

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- mobile first -->
        <title>Colibri VF61 node.js webserver</title>
        <!-- Add jQuery library -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <!-- Using bootstrap -->
        <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="client.js"></script>
</head>

<body>
      <div class="container">
                <h1>Access to the VF61 GPIO using Node.js and AJAX</h1>
                <div class="row">
                        <div class="col-sm-4">
                                <input type="button" class="btn btn-block true_btn" id="LED1">
                        </div>
                </div>
        </div>
</body>
</html>


Now if you run your application, you will see that while there is a major difference in the UI, it stopped working as intended. Before fixing that, let's:

  • Add 2 more buttons inside the row div, changing their id indexes - those buttons will be our LED indicators/controllers
  • Create another row after the one with the LED indicators and add 3 more buttons with id SW1, SW2 and SW3 - they will be the switch status indicators
  • Before the LED indicators, add a row with full width, id "MSG" and value "application running" - it will indicate that our app is running
  • Add some headers before/inside each row to label what are LEDs and switches, so the user can notice it

After doing the changes, you can check the full HTML code here. From now on until the end of the article, this file won't require further changes. Now let's modify the server code to suit our newly developed UI design. Let's also modify the function that configures all pins - as a loop it checks every GPIO as LED or switch and configure it accordingly, so it can be used for any number of pins. For this to be accomplished, we will also create an object for all the GPIO pins used. Below are the parts modified from the previous code:

/* server.js */
/* VF61 GPIO pins */
const GPIO = {  LED1:'47', LED2:'50', LED3:'53',
                          SW1:'46', SW2:'88', SW3:'48'};
 
setImmediate(function cfgOurPins(){
        for(io in GPIO){ //for every GPIO pin
                if(io.indexOf('LED') != -1){
                        cfGPIO(GPIO[io], 'out');
                }
                else if(io.indexOf('SW') != -1){
                        cfGPIO(GPIO[io], 'in');
                }
        }
});


Then, the part that handles incoming POST requests needs to be rewritten. First, as a convention, let's assume that the client will send "getGPIO" as the id if it wants the server to send the current GPIO status. If this is the case, then the server reads all GPIO values and returns them to the client. Otherwise, it is assumed the client will send a pin id and a value to be written to it - in this case, the server will change the GPIO pin state.

Now the server-side code is done and can be checked here, if you wish. But there are still some modifications needed for this application to work: the JavaScript/jQuery client-side code also needs to be modified, and that is what we are going to do now. First of all, the clickHandling function will be renamed and put outside of what we will refer to as the main function of our code and, whenever a true_btn class button is clicked, the btnHandling will be called - this enables us to separate which buttons should toggle the LEDs and which shouldn't. We will also put the AJAX POST request in a function called changeLedState, so our code is easier to understand and this portion of code can be reused:

The main difference from the code without Bootstrap is the checking of the button. Since adding the class btn-success or btn-danger styles the button Green or Red, respectively, we will use this not only to tell the user if the corresponding LED is on/off, but also to check it within our code, by using the indexOf method in the class string of the button, which returns -1 if the string passed doesn't match. Then we change the button class and tell the server to invert the LED state, by calling changeLedState.

Another functionality needed is to read the GPIO state right after loading the page, so that the first LED operations won't be misinterpreted. For that, we will need functions to getGPIO and updateCurrentStatus. The getGPIO will not return the values, but rather have a callback function to which they will be passed; this function will also take advantage of the fact that the server returns the GPIO status when the id "getGPIO" is sent. The updateCurrentStatus only reads every GPIO status returned from the server and changes the corresponding button class. Note that the status of the switches needs to be inverted, because they are active-low; if in the beginning we had configured the /sys/class/gpio/gpiox/active_low to invert the GPIO, we wouldn't need it now. Lastly, it is needed to call the functions inside main to update the buttons’ status.

/* client.js */
$(function main(){
        getGPIO(updateCurrentStatus);
 
        $(".true_btn").click(btnHandling);
});
 
function getGPIO(callback){
        /* Gets the current GPIO status*/
        $.post("/gpio", {id:'getGPIO'}, function (data, status){
                if(status == "success"){
                        callback(data);
                }
        },"json");
}
 
function updateCurrentStatus(gpio_status){
        /* Updates the page GPIO status*/
        if(gpio_status.status == 'readgpio'){
                for (next_pin in gpio_status.gpio){
                        if(next_pin.indexOf("SW") != -1){
                                gpio_status.gpio[next_pin] = !gpio_status.gpio[next_pin];
                        }
                        if(gpio_status.gpio[next_pin]){
                                $("#" + next_pin).attr("class","btn btn-block btn-success").val(next_pin + ":ON");
                        }
                        else{
                                $("#" + next_pin).attr("class","btn btn-block btn-danger").val(next_pin + ":OFF");
                        }
                        if(next_pin == curr_led){
                                $("#" + next_pin).addClass("active");
                        }
                }
        }
}


Then, let's poll the GPIO every 0.2s and add a role for each switch, so we don't get bored! To achieve this, we will use setInterval to call getGPIO periodically and its callback, that we will name swAction, will implement the switches functionality:

  • SW1 will change the selected LED
  • SW2 will toggle the selected LED
  • SW3 will pause the application by stopping the setInterval - that is why the MSG button: to resume the application
  • The MSG button, if clicked when the app is paused, will call resumeApp, which in turn will call setInterval and resume the polling
  • There will also be the need to set two global variables: one to point to the currently selected LED and the other to indicate how many LEDs are there

Try to implement the changes stated above.

Now our demo on how to access the GPIO via a user-friendly web UI using Node is complete. If you wish, you can check the client-side final code here. Check the new interface in the figures 4/5 and notice it is much better-looking than the one presented in the beginning of this section. There is also a video showing the application working.

User friendly application emulated on a smartphone screen

Image 4: User-friendly application emulated on a smartphone sized screen

User friendly application running on a large screen browser
Image 5: User-friendly application running on a large screen browser


Video: Final application working

There are many links mentioned in the article about the concepts we applied, in case you want more information. I also hope that you found this article to be helpful!

Reference

This blog post was originally featured on Embarcados.com in Portuguese. See here.

AuthorLeonardo Graboski Veiga, Toradex Brasil

Subscribe to our future Blog posts:


Leave a comment

Your email ID will be kept confidential. Required fields are marked *

Click to change the Code

Please enter the letters as they are shown in the image above. Letters are not case-sensitive.



* Your comment will be reviewed and then added. Thank you.