Remote controlled environment with node.js

Since some years already JavaScript has become quite a good alternative for server-side programming. It's fast, quite reliable already and the community is ever increasing and more active than ever, putting it in line with consecrated environments like the LAMP stack or Django + nginx + fcgi + MySQL.

From all the major Server-Side JavaScript implementations available today, node.js seems to be the most popular at this moment. Besides coming with its own package management tool (npm), what puts it in front of its counterparts is the ease of access to hardware system resources. This has the advantage of removing unnecessary intermediary layers that are most of the times required in order to achieve this purpose, by providing one common environment and programming language for front- and back-end, as well as server- and client-side programming.

One of the myriad of possible problems that node.js could solve is the creation of a "remote control" web application paired with another (web) application that needs to be controlled from the distance, in real-time.

1. Why would I need that?

  1. A real world example would be triggering click events on a remote web page from another computer / device / location, for simulation or presentation purposes.
  2. Another (even more fun) application would be to use this implementation to control a Raspberry PI with node.js on it and access its GPIO (General Purpose Input Output) ports. From there on sky is the limit, be it a full featured robot or just ordering your coffee machine to make warm coffee for you just before you get home from work!

In this article, we will try to address mostly the first use case above, but we will make sure we link enough resources for you to start investigating the second option, too.

NOTE: Since we built this application just for simple testing / presentation purposes, we didn't bother with separating the command signals, which means that if you have one remote and multiple opened receivers, a command sent by the remote will be processed and executed on all receivers.

2. Building the remote control app

This will apply to both use cases suggested above, regardless of what is going to be controlled. The only requirement for the receiver app will be that it can listen to messages transmitted via Socket.io.

The main application (the "receiver") can be controlled via a set of pre-defined, configurable events. The events are defined in a JSON configuration file and are called via a "remote control" web application. Once called on the remote control, the events are triggered in the public application.

The technology we would suggest for the remote control application is based on a node.js + express + socket.io environment. The first two components should provide enough extensibility for further development, whereas the third provides the true real-time connectivity that we need, regardless of the device and browser (from IE 5.5 up to the latest mobile devices). As for template rendering, we used the lightweight mustache, for both the front- and back-end of the app. However, you can use whatever templating system you like (express is used mostly with jade or if you are a Django fan as I am you could use swig).

In order to transmit the action to be performed by the receiver, we need to define a command format that will be recognized by both components. We chose JSON for this. The JSON string will be sent via socket.io's send() method and will be received by the "public" app through the message event.

Starting the server and making it listen

The following snippet suggests a possible way to start the node.js + express + socket.io server. Please note that you will still need to configure the application according to your needs.

var express = require('express'),
    http = require('http'),
    io = require('socket.io'),
    nconf = require('nconf'),
    admin = require('./controllers/admin'),
    index = require('./controllers/index');

// Create the HTTP server
var app = express(),
    server = http.createServer(app);

app.configure(function () {
    app.set('port', 8080);
    /* Application configuration (node / express) */
    // ...
});

app.configure('development', function () {
    app.use(express.errorHandler());
});

// Making the commands list available to the controllers
app.locals({
    commands: nconf.file('public/conf/actions.json').load()
});

// Routes
app.get('/', index.index); // Receiver app
app.get('/admin', admin.index); // Remote control app

// Initialize the HTTP server
server.listen(app.get('port'), function () {
    console.log("Express + Socket.io server listening on port " + app.get('port') + '...');
});

// Finally, create the Socket.io server and initialize it
var sio = io.listen(server);
sio.sockets.on('connection', function (socket) {
    socket.on('message', function (msg) {
        socket.broadcast.emit('message', msg); // Sending the message to everybody but this emitter
    });
});

Command format

A JSON command can have a prototype similar to this:

{
    item:       '.my-button', // element selector
    action:     'click'       // event to be triggered
}

Translated in plain English, the above command should sound like this:
"Select all elements with selector .my-container. Trigger the click event on all of them."

Everything is pretty standard, and since we choose jQuery to work with the front-end app, triggering the desired event would be easily translated into a nice jQuery one-liner.

However, you don't need to stick to standard events like click, submit or the like. For example, a more customized command could look something like this:

{
    item:       '.my-message', // element selector
    action:     'populate', // event to be triggered
    value:      'Some message text' // any value
}

The above command should perform the following:
"Select the elements with selector .my-container. Populate all of them with the text assigned to the value object propriety."

Of course, in the front-end you will have to first define the custom event populate(event, value), which will use jQuery's html() or text() method to populate an element with HTML code or text. Then you just need to trigger the event populate whenever you receive this command.

Command sending

In order for the actions to be as configurable as possible, we put them in an array defined in a separate JSON file which, as you could see, we read with some help from node's nconf module, which is supposed to read the file and return a nice JSON object for future processing.

The JSON object is then parsed and we generate a set of links that - upon clicking - should actually call socket.io's send() command. We used jQuery Mobile for the actual remote control interface and we made use of the data attribute to store the command parameters, like this:

<a href="#" class="command-trigger" data-command-item=".my-message" data-command-action="populate" data-command-value="Some message text">
    {{ob.command.value}}
</a>

Then, we defined the click event on such a link like this:

$('a.command-trigger').on('click', function(e) {
    e.preventDefault();
    // We recompose the actual command for sending via Socket.io
    var command = {
        item: $(this).data('command-item'),
        action: $(this).data('command-action'),
        value:$(this).data('command-value')
    };
    iosocket.send(JSON.stringify(command));
});

3. Implementation on the public (receiver) app's side

To power the front-end app, we choose jQuery for working with the DOM and trigger the events sent by the remote control app.
Triggering the events corresponding to the received command is very simple:

Receiving the command

var iosocket = io.connect();
iosocket.on('connect', function () {
    iosocket.on('message', function(message) {
        var command = JSON.parse(message);
        // This is where the magic is!
        $(command.item).trigger(command.action, [command.value]);
    });
});

Triggering the corresponding event

In order to instruct jQuery what to do when a certain event is triggered, you need to define the events (maybe in a separate script that you include in the head of your HTML template layout file):

$('button.my-button').on('click', function(e, value) {
    // Do something whenever a button is clicked
    // ...
});

As far as custom events are concerned, we could either indicate a more specific selector or they could be triggered on any element, so we take advantage of event bubbling and declare the event on a top-level element:

$('body').on('populate', function(e, value) {
    // The element that we are actually targeting
    var $target = $(e.target); 
    if($target.length) {
        // Populate the text node of the targeted element
        $target.text(value);
    }
});

4. Wait! You said something about pies and robots...

Well, yes. As you probably know already, node.js is not just part of yet another web stack, but it also gives you access to system hardware resources, like reading or writing the serial port, the USB ports or - since Raspberry PI or Arduino are providing them - the GPIO (General Purpose Input-Output) ports. This means that, as long as somebody wrote some wrapper libraries for node, you should be able to directly access external hardware resources through these ports, like a web camera, some temperature or light sensors, some motors or just an array of LEDs. This brings us to something that we didn't even dare to dream a few years ago: controlling all kinds of electronics via the Internet in a simple and cheap manner.

So, the idea detailed in this article could be easily adapted so that instead of triggering clicks in a web page you trigger anything from a simple LED light to a robot.

In order to do this, you would need to have access to a Raspberry PI on which you install node.js with all the necessary modules and the remote control application described above. (See here how. You may need to read this thread, too.) Only that, instead of building a web application as a receiver, you can build a very simple node.js app that doesn't do anything but send a signal on just one of the GPIO ports, to which you're connected your LED. This application will, of course, have to listen to a socket.io transmission and perform the desired operation according to the command you're sending.

We have linked some software and documentation resources in the Resources chapter.

5. Conclusions

So, this should be it! We now have a back-end part that is generating some links from a custom commands JSON configuration file, links that when clicked (or tapped) send the corresponding command via socket.io to a front-end receiver application, which will trigger the events defined in the received command.
The awesome part is that the receiver could be another web application but also much more advanced node.js-based software that controls the GPIO ports of an Arduino or a Raspberry PI, allowing you to gracefully step into the world of robotics!

We hope that the explanations and code snippets above gave you enough ideas to start building awesome real-time node.js applications!

6. Resources

Blog Category: 
Tech News
Bild des Benutzers Valeriu.Lungu