Creating a New Hardware Interface

This tutorial will show you how to create a new Hardware Interface in an add-on.

Setting up a new add-on

If you’ve already gone through the Spatial Tool tutorial, you can reuse the addon called my-addon that you created in that tutorial. If not, create a new directory called my-addon in the vuforia-spatial-edge-server/addons directory.

Now, create a directory called interfaces in my-addon. Each directory within interfaces will be a separate hardware interface – a new “plugin” for this server to communicate with an external system.

Create the index.js file

Create a directory in interfaces called testInterface, and then create a file called index.js inside testInterface. The index.js file of each hardware interface will get executed by the Edge Server when the Node.js environment finishes initializing the Edge Server’s core capabilities. This file is isolated from the rest of the server with the exception of the server’s libraries/hardwareInterfaces.js file, which provides an API that hardware interfaces can use to bridge the functionality of the server to external hardware.

All hardware interfaces can be set up with the following template:

// include the hardware interface API module from the server
let server = require('@libraries/hardwareInterfaces');

// load any persistent settings for this interface, such as whether it is enabled
let settings = server.loadHardwareInterface(__dirname);

// export these so that the hardware interface can be configured on the web interface
exports.enabled = settings('enabled');
exports.configurable = true;

if (exports.enabled) {
    // place your interface code here
    // doing so properly ignores this interface if it has been disabled on the web interface
}

Using the Hardware Interface API

Inside the template, you will need to access some combination of the Hardware Interface APIs based on what you want this interface to do.

In general, implementing a hardware interface looks like this:

  1. Determine which nodes you want to connect to the hardware. This can be hard-coded or loaded from external settings.
  2. Implement a hardware-specific method for how to send data to that piece of hardware when data arrives at one of these nodes.
  3. Implement a hardware-specific method to listen to new values being generated by the hardware , and to send those values into the Spatial Toolbox system.

The corresponding APIs, which are only a subset of the provided APIs but are the most common ones to use are:

(Clicking on these will bring you to their API documentation)

If you’ve already gone through the Spatial Tool tutorial, you’ll notice that these are similar (but not identical) to the APIs available to tools.

Each of these functions has three parameters for object, tool, and node – the combination of which lets you specificy the address of a specific node on a speciic frame of a specific object.

addNode lets you create a new node on a local frame (one that you created through the server’s web interface, not one dropped in from the pocket) with a specific name.

addReadListener lets you subscribe to new data reaching this node from other tools and links in the system, so that you can send each new data packet to the hardware.

write lets you write a new data packet to the node whenever you hear from the hardware, so that the value will propagate across links to other nodes. You’ll need to set up your own method to listen to values from your hardware so that you know when to trigger this.

Looking at an example: Kepware Interface

Lets look at a simplified implementation of the Kepware hardware interface as an example. The Kepware interface uses a REST API to the KEPServerEx IoT Gateway to read and write data to Industrial IoT hardware.

let server = require('@libraries/hardwareInterfaces');
let settings = server.loadHardwareInterface(__dirname);
exports.enabled = settings('enabled');
exports.configurable = true;

if (exports.enabled) {
    let objectName = 'myHardwareObject';
    let frameName = 'myHardwareFrame';

    // 1 - create a node named 'value' on the frame..
    // (this will only create it if it doesn't already exist)
    server.addNode(objectName, frameName, 'value', 'node');

    // 2 - you may need to include different node modules based on the protocol your hardware uses
    let Client = require('node-rest-client').Client;
    let remoteDevice = new this.Client();
    let IOT_GATEWAY_URL = 'http://10.10.10.10:39320/iotgateway/';

    // 3 - when data arrives at this node, send it to the hardware
    // in this case we're using a REST API but could use bluetooth, serialport, etc
    server.addReadListener(objectName, frameName, 'value', function(data) {
        let args = {
            data: [{id: 'value', v: data.value}],
            headers: { 'Content-Type': 'application/json' }
        };
        this.remoteDevice.post(IOT_GATEWAY_URL + 'write', args, function (_data, _res) {
            console.log('successfully sent data to hardware');
        }).on('error', function (err) {
            console.warn('error sending data to hardware', err);
        });
    });

    let updateRateInMilliseconds = 100;
    let previousValue = null;

    // 4 - set up an interval to periodically check if the hardware has changed
    setInterval(function() {
        remoteDevice.get(IOT_GATEWAY_URL + 'read?ids=value', function (data, _res) {
            let newValue = data.readResults[0].v;
            if (newValue !== previousValue) {
                // if we have a new value, write it from the hardware to the node
                server.write(objectName, frameName, 'value', newValue);
                console.log('received new data from hardware');
                previousValue = newValue;
            }
        });
    }, updateRateInMilliseconds);
}

This won’t be completely functional – there are a lot more details in the Kepware hardware interface in order to make this really work, but the main logic is all there. In step 1, we add a node with a specific name that we will use for all of our communication between the Vuforia Spatial Toolbox and the external hardware system. In step 2, we set up our environment by including the Node.js modules needed to communicate in the protocol that the hardware system uses. In step 3, we add an event listener for data arriving to our node from other parts of the Vuforia Spatial Toolbox system, and when that happens we forward the data to the hardware. In step 4, we ask the hardware for its current value 10 times per second, and if the value ever changes we write it to the node so that it propagates throughout the Vuforia Spatial Toolbox system.

More APIs

There are a lot more APIs that haven’t been explored yet in this tutorial, but you can find them all listed on the API Reference page. Hardware interfaces are extremely flexible with what you can do with them. An advanced hardware interface developer will be able to extend the Vuforia Spatial Toolbox in new and unexpected ways.

Making it configurable

One big difference between this and the actual Kepware implementation is that we’ve hard-coded a lot of data (the objectName, the frameName, the IP address of the KEPServerEX, etc), whereas the full implementation makes use of configurable settings so that these parameters can be specified using the server’s web interface. To learn how to add those, you can read the advanced-level tutorial: How to add configurable settings to your hardware interface.

Seeing your object

At this point, the Vuforia Spatial Edge Server knows that your hardware exists and will happily propagate changes between the hardware and the object representing it. However, the server doesn’t know where your object is in the physical world. To allow the Spatial Toolbox to orient the object in space, it’s time to associate it with a visual marker.

Open up the Server’s web interface at http://localhost:8080 and click “Add Object.” Give this new object the name you chose in your hardware interface (“myHardwareObject” above) then drag and drop an image to use as a marker. Now if you look at this marker you can see and position your object’s nodes in space.

For more information about creating objects, check out the How to create an Object tutorial.