Skip to content

Project From Scratch

wwitman edited this page Jan 7, 2016 · 1 revision

Build a zetta project from scratch

Part 1: Thinking about Zetta

Zetta is a Node.js application that allows clients to interact with physical devices like microprocessors, robot arms, remote sensors, and so on across the internet. It's an elegant and approachable IoT platform.

Zetta's main Node.js component exposes a bunch of methods for configuring and managing Zetta's built-in HTTP server and for generating device-specific APIs.

A Zetta server can run locally as a "hub" connecting devices through a LAN. Local hubs can be linked to other hubs on the same LAN and linked to other Zetta instances running in the cloud. Linking allows you to control devices from anyplace with an internet connection.

For example, Zetta has a name() method that assigns a name to a Zetta instance using the method chaining pattern:

var zetta = require('zetta');
zetta()
    .name("Waldo");

Much more interesting, Zetta has an embedded HTTP server. To configure the server, add the listen() method to the chain with a port number argument and an optional function, like this:

var zetta = require('zetta');
zetta()
    .name("Waldo");
    .listen(1337, function() {
         console.log('Zetta is running at http://127.0.0.1:1337');
     });

Here are more details on the listen() method. You can find all the Zetta methods documented in the Reference.

Method:listen(port, [hostname], [callback])

  • port - The port number on which the Zetta server listens.
  • hostname (optional) - The hostname where the server is running.
  • callback (optional) - A function that executes when the server starts (when the listen event fires).

Let's fire pu this server and see what it does. If you're a Node enthusiast, you'll be very familiar with these steps:

  1. Create a project directory. Let's call it zetta-demo-project.

  2. In a text editor, create a new file called index.js.

  3. Copy the preceding code into the file and save it.

  4. Install the single dependency we have so far:

    npm install zetta

  5. Start the server:

    node index.js

When the server starts up, it automatically scouts (looks around) for devices. At the moment, we don't have any devices, so none are found. We'll talk about devices shortly.

Here's the output you get, and it just confirms that the server is running:

Jan-05-2016 09:38:56 [server] Server (Will-Witmans-MacBook-Air.local) Will-Witmans-MacBook-Air.local listening on http://127.0.0.1:1337
Zetta is running at http://127.0.0.1:1337

Finally, call the server from a browser, like this:

http://localhost:1337

Here's the output (with main JSON elements collapsed). The main thing to notice is that it's a JSON object, and that it contains hypermedia links that let clients "discover" what the device can do and to perform actions.

{
  class: [...],
  links: [...],
  actions: [...]
}

At this point, the API can't do much of anything, but we'll fix that as the tutorial progresses.

Part 2: Create a device

In Zetta, a Device is a JavaScript object that models a real-world device. In this tutorial, we're going to model our device as a simple state machine that can turn an LED attached to a BeagleBone Black processor on and off.

If you have a BeagleBone Black, great! You'll have LEDs blinking in no time. If not, we'll try to keep it interesting with short videos that demonstrate our progress. The goal of this tutorial is to teach you the fundamental programming patterns you can apply to any Zetta device driver.

Let's get started. We'll code up a generic starter device and try it out. Later, we'll modify the starter device so that it can interact with the BeagleBone's external LEDs.

  1. First, cd to the directory we created, zetta-led-bonescript-driver.

  2. In a text editor, create a file called led.js (we typically give device files a name that reflects the physical device).

  3. Add the core dependencies for a device:

    var Device = require('zetta-device');
    var util = require('util');
    
  4. We need to install the zetta-device dependency:

    npm install zetta-device

  5. Add the device constructor. Don't worry about the options and properties for now. By inheriting from Device, we obtain the API generation and other functionality from the zetta-device module.

    var Led = module.exports = function(options) {
       Device.call(this);
    };
    
    util.inherits(Led, Device);
    
  6. Configure the device's init() function. For now, we'll just add two properties: name and type. Later, we'll configure functions that manage the state of the device.

    Led.prototype.init = function(config) {
      config
      .name('Led')
      .type('led')
    };
    

    Now we have a rudimentary driver. But the Zetta server doesn't know about it. Let's change that.

  7. Save the device file, led.js.

  8. Open the server file, which we called index.js.

  9. Now, we'll add add two lines to the the server. First, we need to require the device file. Then we add one line of code to the chain to "use" the device, like this:

    var zetta = require('zetta');
    var Led = require('./led.js');
    
    zetta()
      .name("Waldo")
      .use(Led)
      .listen(1337, '127.0.0.1', function(){
         console.log('Zetta is running at http://127.0.0.1:1337');
    });
    
  10. Start the server: node index.js

The console output indicates that the device called led was discovered, and the server is running:

Jan-05-2016 12:01:30 [scout] Device (led) c0e3150a-3492-406f-b102-7ee03dc0a7c0 was discovered
Jan-05-2016 12:01:30 [server] Server (Waldo) Waldo listening on http://127.0.0.1:1337
Zetta is running at http://127.0.0.1:1337

Summary

So far we've configured and started a Zetta server and a Zetta device. The coding patterns we've used are going to be the same for every Zetta project. We've seen the minimal configuration that a device needs to be "discovered" by Zetta.

In the next part, we'll incrementally add device-specific code to the device. We'll identify the device's state transitions and define methods for changing the states.

Part 3:

Now we'll dive a little deeper into Zetta device methods. The goal is to specify the device's state transistions and create functions that are called on each transition.

We'll add these methods to the device configuration:

  • state -- Defines the current state of the device. Example:

    .state('on')

  • when -- Sets which transitions are available when the device is in a partiular state. This method takes two arguments: (1) a valid state and (2) an object with a property called allow that lets you define the available transitions for the state. Example:

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

  • map -- Maps transitions to functions. Whenever a transition is called on a state, the corresponding function executes. Examples:

    .map('on', this.turnOn);
    .map('off', this.turnOn);
    

Let's try it out.

  1. Open led.js and copy/paste this code into it. The config methods are pretty self-explanatory, and the methods for turning the device on and off are trivial, but they get the point across, and give you a functioning Zetta project with a mock device capable of changing state from off to on, back and forth.

    var Device = require('zetta-device');
    var util = require('util');
    
    
    var Led = module.exports = function(options) {
       Device.call(this);
       //this._default = options['default'];
    };
    
    util.inherits(Led, Device);
    
    
    Device.prototype.init = function(config) {
      config
        .type('led')
        .name('Led')
        .state('off')
        .when('off', { allow: ['switch-on']})
        .when('on', { allow: ['switch-off']})
        .map('switch-off', this.switchOff)
        .map('switch-on', this.switchOn);
    };
    
    Led.prototype.switchOn = function(cb) {
      this.state = 'on';
      cb();
    };
    
    Led.prototype.switchOff = function(cb) {
      this.state = 'off';
      cb();
    };
    
    
    
    
  2. Start the Zetta server: node index.js

  3. Open the Zetta instance in the Zetta browser client. The browser client is a convenint tool provided with Zetta that lets you interact with a Zetta device. In a browser, hit the following URL:

    http://browser.zettajs.io/#/overview?url=http://127.0.0.1:1337

You should be able to turn the device on and off in the browser tool:

<< Insert Picture >>

Part 4: Check out the API:

  1. In a browser or a REST tool like Postman or Advanced REST Client, enter the URL of your Zetta instance and hit Send. The API now includes some interesting links. Let's explore them.

Part 4. Exploring the Zetta HTTP hypermedia response

<< Insert section on hypermedia. >>

Part 5: Introduce the BoneScript module

For this part of the tutorial, we'll begin interacting with a real device, a BeagleBone Black processor. If you have a BeagleBone Black, great! You'll have LEDs blinking in no time. If not, we'll try to keep it interesting with short videos that demonstrate our progress. The goal of this tutorial is to teach you the fundamental programming patterns you can apply to any Zetta device driver.

Getting started

  • We assume that you have a BeagleBone Black device up and running as described in the Getting Started instructions on the BeagleBone website.
  • We assume you have configured your device to connect to your laptop through the USB network adapter provided with the BeagleBone. You can find tutorials and videos like this one explaining this setup on the internet.
  • You're running the cloud9 IDE in a browser on the laptop that is connected to your BeagleBone.
  • You've moved a copy of this tutorial project over to the cloud9 IDE.

Using BoneScript

BoneScript is a Node.js module that lets you interact with a BeagleBone processor using JavaScript. It's pretty straightforward, built in to the cloud9 IDE, and includes methods for turning output pins on and off, and so on.

Let's explore and adapt our mock device to workk with the BeagleBone!

  1. Open the led.js device, and require BoneScript:

    var bonescript = require('bonescript')

Clone this wiki locally