Skip to content

LED Device

wwitman edited this page Dec 18, 2015 · 3 revisions

Introduction

In this tutorial we'll walk through the steps to create a very basic Zetta device. When you're finished, you'll understand the basic coding pattern used to create any Zetta device.

What is a Zetta device?

A Zetta device is software that models state and behavior for a physical device, such as an LED, a photocell, a robot arm, a car, or anything else you want to be able to control with a web API.

##About the device programming interface

Zetta device drivers are written in Node.js (JavaScript). For some device platforms, such as Beaglebone Black and Edison, Node.js modules are easy to obtain and allow you to program your Zetta device directly and intuitively. For example, the BoneScript module provides a Node.js interface to Beagle devices, such as BeagleBone Black.

To find a list of existing drivers that are on NPM, you can use the Zetta module search site. For more information on finding existing drivers that are in the public domain, see Searching for Zetta modules.

Use a REST client

You'll have the best experience with this tutorial if you have a REST client like Postman or Advanced REST Client to try out the Zetta-generated REST APIs. These kinds of REST clients render responses with links you can click, making it easy to exercise a REST API. If you want to use cURL, you can try a utility, like the python JSON formatter that allow you to output readable results. For example:

curl http://localhost:3000 | python -m json.tool

Start from the beginning

Let's start with a new Zetta project. The best thing to do is follow the simple instructions in the first tutorial topic: Hello, Zetta!

Here's a shorthand list of the steps:

  1. mkdir myproject

  2. cd myproject

  3. npm init (and press return several times to create a default project)

  4. npm install zetta --save

  5. Create a file called index.js and add this code to it and save it:

  var zetta = require("zetta");
  var led = require("zetta-led-mock-driver");

  zetta()
   .name("Device Tutorial")   
   .listen(3000, function(err) {
     console.log("Zetta server is running on port 3000");
   });
  1. node index.js

  2. If you see this output in the terminal, you're good to go:

Oct-23-2015 10:38:41 [server] Server (Device Tutorial) Device Tutorial listening on http://127.0.0.1:1337
Zetta is running at http://127.0.0.1:1337

Create the simplest device

Now that we have a functioning Zetta server, let's create the simplest possible device possible and configure the Zetta server to find the device. As you'll see, this basic pattern requires only a few lines of code. From this starting point, we can build a more interesting device.

  1. cd to the Zetta project you created, as explained above.

  2. Create a new file called device.js.

  3. Copy this code into the file and save it:

var util = require('util');
var Device = require('zetta').Device;

var Dev = module.exports = function DeviceDriver() {
  Device.call(this);
}

util.inherits(Dev, Device);

Dev.prototype.init = function(config) {
  config
    .type('fake-device')
};
  1. Open the index.js file and add this line at the top:

var DEVICE = require("./device.js");

  1. Now, use the fake device by calling .use(DEVICE) on the zetta() object. Your server code should look like this:
var zetta = require("zetta");
var DEVICE = require("./device.js");

zetta()
   .name("Device Tutorial")
   .use(DEVICE)
   .listen(3000, function(err) {
     console.log("Zetta server is running on port 3000");
   });
  1. Start the server: node index.js. You will see this output in the terminal:
Sep-10-2015 15:27:10 [scout] Device (fake-device) 1d93c7b1-aa00-488f-b738-a9116f33efb7 was discovered
Sep-10-2015 15:27:10 [server] Server (Device Tutorial) Device Tutorial listening on http://127.0.0.1:3000

Quick Summary

With just a few lines of code, you have a running Zetta server that has discovered the simple device you wrote. The file device.js illustrates the basic pattern you'll see for all Zetta devices:

  • Require the util and zetta npm modules.

  • Export a function that inherits from zetta().Device

  • Write an init() function that takes a DeviceConfig object. As you'll see, there are many more methods you can call on the DeviceConfig object for setting device state, configuring state transitions, mapping state transitions to functions, streaming data, and more.

So far, our device doesn't do anything, but we'll add code fix that next. We'll configure a mock device that blinks a mock LED. Once you understand how the mock device operates, wiring up a real device, such as an LED that blinks via Beaglebone Black hardware, is fairly easy. We'll show you how to do that in another tutorial.

Configure the device

  1. In a text editor, open device.js:
var util = require('util');
var Device = require('zetta').Device;


var Dev = module.exports = function DeviceDriver() {
  Device.call(this);
}

util.inherits(Dev, Device);

Dev.prototype.init = function(config) {
  config
    .type(fake-device)
};
  1. Let's make a few changes and call some new methods on the DeviceConfig object:

    1. Change the type from fake-device to led.

    2. Set the initial state of the device to off: .state('off')

    3. Give the device a name: .name(led)

    4. Tell the device what transitions it allows for a particular state. In the case of an LED, when the state is "off", allow it to transition to "on", and vice versa. We express these transitions as follows:

    .when('off', { allow: ['turn-on'] }) 
    .when('on', { allow: ['turn-off'] }) 

    As we'll see, these transitions will show up as "actions" in the device API.

    1. Now, we map these allowable transitions to functions that control the device. For now, we'll set these map function references to null and take a quick look at how the API has changed. Then, we'll write the map function code in the next section.

      Note: All of the methods for the DeviceConfig class are documented in the Device reference.

    2. Make sure your code looks like this:

    var util = require('util');
    var Device = require('zetta-device');
    
    var LED = module.exports = function(led) {
      Device.call(this);
    };
    util.inherits(LED, Device);
    
    LED.prototype.init = function(config) {
      config
        .type('led')
        .state('off')
        .name('LED')
        .when('off', { allow: ['turn-on'] })
        .when('on', { allow: ['turn-off'] })
        .map('turn-on', null)
        .map('turn-off', null);
    };
  2. Save the file.

  3. Start the server: node index.js

  4. In a REST client tool such as Postman or Advanced REST Client, hit the server URL: http://localhost:3000

  5. Find the servers link and hit it: http://localhost:3000/servers/Device%20Tutorial

  6. Now, find the href link in the led device entity. Follow this link. It takes you to the API for the device, which includes the available actions the device can take. It should look something like this: http://localhost:3000/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce

    Notice the properties attribute indicates the current state is off. And the available action that can be performed on this device is turn-on.

      {
        "class": [
            "device",
            "led"
        ],
        "properties": {
            "id": "6fe9f109-9778-41be-9de9-c6e46b652cce",
            "name": "led",
            "type": "led",
            "state": "off"
        },
        "actions": [
            {
                "class": [
                    "transition"
                ],
                "name": "turn-on",
                "method": "POST",
                "href": "http://localhost:3000/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce",
                "fields": [
                    {
                        "name": "action",
                        "type": "hidden",
                        "value": "turn-on"
                    }
                ]
            }
        ]

Next, we'll implement the methods for turning the LED on and off. Then, we can use the API to execute those methods.

Implement the transition functions

  1. Open device.js in an editor.

  2. At the bottom of the file, add these two methods:

    Dev.prototype.turnOn = function(cb) {
      this.state = 'on';
      cb();
    };
    
    Dev.prototype.turnOff = function(cb) {
      this.state = 'off';
      cb();
    };
  3. Now, edit the map() methods, replacing null with these method names: .

    .map('turn-on', this.turnOn)
    .map('turn-off', this.turnOff)
  4. Save device.js and restart the Zetta server.

  5. Go back to your REST client and walk back through the links, starting with http://localhost:3000, then the servers link http://localhost:3000/servers/Device%20Tutorial, and then the link for the device, http://localhost:3000/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce.

  6. You can see in the response, that the current state is off, and the transition action turn-on is available. To execute this action, you need to form a POST request. Here's how you can make the request in cURL (although your actual device ID will be different):

    curl -i -X POST http://127.0.0.1:1337/servers/Hello%20Zetta/devices/e6f5b480-e96e-4fdc-8718-91aeb0234c99 -d ‘action=turn-on’

  7. Check out the response. Notice that the transition has changed from turn-on to turn-off:

"actions": [
        {
            "class": [
                "transition"
            ],
            "name": "turn-off",
            "method": "POST",
            "href": "http://localhost:3000/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce",
            "fields": [
                {
                    "name": "action",
                    "type": "hidden",
                    "value": "turn-off"
                }
            ]
        }
    ]

####Quick Summary

In this part, we walked through the steps for wiring up a mock device that switches state on and off. We saw how you can interact with the device through it's RESTful Web API. Client app developers can code apps that call this API to control the device.

Next step

Check out How Drivers Work, a short tutorial that explains how to create a simple LED driver for the Beaglebone Black.

Clone this wiki locally