Skip to content

whouses/rotonde

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rotonde

  • rotonde makes IoT development accessible to every developers.
  • just pick your favorite language and start coding on your favorite platform.
  • with rotonde you use the best libraries available, not just the one available for your favorite language.
  • and it's as easy to use as a web API:)

Live example

  • Go through the setup guide.
  • Install this module.
  • Run both of them, on your raspberry PI for example, but these two can work on you laptop too, not sure about Windows tho...
  • Install this chrome extension.

TODO: make install+run script

Now from the extension connect to rotonde on your board's IP:

ws://[ip address]:4224/

Click open, you are now connected to rotonde.

In the extension's console you should have something like this (stripped most of them for clarity):

[...]
{  
   "type":"def",
   "payload":{  
      "identifier":"SERIAL_PORTMESSAGE",
      "type":"event",
      "fields":[  
         {  
            "name":"P",
            "type":"",
            "units":""
         },
         {  
            "name":"D",
            "type":"",
            "units":""
         }
      ]
   }
}

{  
   "type":"def",
   "payload":{  
      "identifier":"SERIAL_OPEN",
      "type":"action",
      "fields":[  
         {  
            "name":"port",
            "type":"string",
            "units":""
         },
         {  
            "name":"baudrate",
            "type":"number",
            "units":""
         },
         {  
            "name":"buffer",
            "type":"string",
            "units":""
         }
      ]
   }
}
[...]

These are the available events and actions. All those starting with SERIAL_ are from our serial module.

We are going to use it and open a serial port on the system by sending a SERIAL_OPEN action.

Clear the websocket output and copy-paste this JSON object into to the websocket extension:

{
  "type":"action",
  "payload":{
    "identifier":"SERIAL_OPEN",
    "data":{
      "port":"/dev/ttyAMA0", // internal serial port on raspberry PI
      "baudrate":9600
    }
  }
}

hit ctrl+enter to send it to rotonde.

If your serial port is opened you can now listen to it by subsribing to the SERIAL_PORTMESSAGE by sending this packet:

{
  "type":"sub",
  "payload":{
    "identifier":"SERIAL_PORTMESSAGE"
  }
}

And that's it, you just opened a serial port on you raspberry PI, that's as easy as that with rotonde.

Rotonde mindset

When you code with rotonde you have to think in term of modules. Each module is connected to rotonde, and communicate with each other though rotonde.

Events and actions

In rotonde each module exposes its API through events and actions. Events and actions have similarities in their behavior, but they represent two different concepts.

  • Events:
    • sent by modules to notify the rest of the system
    • other modules can subscribe to them
  • Actions:
    • the API of the available modules
    • sent by modules to call features from other modules

Modules first have to declare their events and actions to rotonde.

Declaring an event or action to rotonde is achieved by sending a def packet, please see the def section below.

When a module exposes an action or event to rotonde, it has to specify an identifier and a list of fields.

The first thing that a module receives when connecting to rotonde is the list of all events and actions that have been defined on rotonde by other modules.

This gives modules the opportunity to check that the system they are connected to has the required features available.

Events and actions routing

Internally rotonde works like some kind of a router or dispatcher. The routing rules are slightly different for actions and events.

When rotonde receives an action from a module, it looks at all modules that exposed this actions, and dispatches the action to them.

When rotonde receives an event from a module, it only dispatches it to the modules that subscribed to this event.

Setup

Requirements

  • some unix os (tested with success on Linux and OSX so far)
  • Golang (1.5.1, please tell us if you got it working on previous versions, we didn't test them yet)
  • Godep

Compilation

Assuming Golang had been installed, if it's not already done a workspace can be set with

export GOPATH=$HOME/go
mkdir $GOPATH
go get github.com/HackerLoop/rotonde && go get github.com/tools/godep
cd $GOPATH/src/github.com/HackerLoop/rotonde
godep restore
go build

go build will compile an executable called rotonde in the project folder ($GOPATH/src/HackerLoop/rotonde).

If something goes wrong during compilation, please open up an issue with your os/distribution infos and compilation output.

Running

./rotonde

Rotonde will start serving on port 4224 by default, add option -port to specify another one.

JSON protocol

In most case, rotonde is used through its websocket (other interfaces are foreseen), by sending and receiving JSON objects.

All objects received or sent from/to rotonde have this structure:

{
  type: "",
  payload: {
    // mixed, based on type
  }
}

There a five possible values for the type field, event, action, def, sub or unsub, the content of the payload field varies based on the type.

The different possible structures of the payload are described below.

Def

When a module connects to rotonde, it has to detail its API to rotonde. It does so by sending definition objects, rotonde routes all received definitions to all connected modules. And when you connect to rotonde, you receive all the available definitions.

Knowing that everything is either an action or an event in rotonde, there are two types of definitions, either action or event.

Each definitions contain a set of fields describing its structure. This is mainly to ensure that the action or event is present with a predictable structure.

In a typical scenario, a module that connects to rotonde starts by waiting for all the events and actions it requires to work properly. Definitions are a sort of description of what is available on the system.

This way of doing things gives you the ability to create a modular system where some module would only start when a given capability is present.

{
  "identifier": "",
  "type": "",
  "fields":[
    {
      "name": "",
      "type": "", // optional
      "unit": "", // optional
    },
    {
      ... other fields ...
    }
  ]
}

Action

In rotonde, everything is either an action or an event, they are the only way for the modules to exchange data with the external world.

Actions are the APIs of the modules, each action has an identifier; when a module declares an action through the mean of a definition object, it will receive all actions matching this identifier sent to rotonde. If multiple modules declare the same action, they will all receive it.

Actions are typically sent by user interface modules, for example, when a user presses a button, the controller of the button will send an action, that will be handled by one or multiple modules. For example, if the button is meant to switch a light on, the action identifier would be TURN_LIGHT_ON, this could totally trigger the light control module, but if we want to play a music when this happens, just do another module that also exposes the TURN_LIGHT_ON action, and starts the music when it receives the action.

An action generally comes with some data, this data can be of any form, its structure should be described by the definitions.

An action payload contains the following fields:

{
  "identifier":"",
  "data": {
    ... data fields of the action ...
  }
}

Actions can be seen as the input of modules.

Event

Modules will often have things to say, whether they want to tell what there sensor is sensing, or whether they want to report a status.

This is what events are made for. Modules have the ability to send events through rotonde, these events will be routed to the concerned modules.

An event generally comes with some data, this data can be of any form, its structure should be described by the definitions.

An event payload contains the following fields:

{
  "identifier":"",
  "data": {
    ... data fields of the event ...
  }
}

Events can be seen as the output of modules.

Sub / Unsub

When you connect to rotonde nothing will be received except definitions, you have to subscribe to a given event in order to start receiving it.

{
  "type": "sub",
  "payload": {
    "identifier": ""
  }
}

and you can unsubscribe from this identifier with:

{
  "type": "unsub",
  "payload": {
    "identifier": ""
  }
}

Abstractions

Rotonde can be used as-is but having an abstraction above the websocket, makes it much more efficient.

This is a list (in progress) of the available abstractions:

Libraries to port to rotonde

The number of available modules is crucial for rotonde, the good news is that it is actually quite easy to take a library and make its API available to rotonde modules.

Here is a list of libraries that are ported, or will be ported:

  • Serial:
    • rotonde serial module exposes the features of serial-port-json-server(SPJS)
      • serial devices listing
      • advanced serial communication
      • flashing of an arduino from an url :) this one is crazy haha.
      • please see his README first
  • Gobot:
    • This is a work in progress and a huge gain for rotonde, gobot already has the ability to make programs that expose features through a REST API, and because it is written in Go, it is really easy to just swap REST to websocket.

      The plan here is to generate tons of modules for all of gobot drivers, in all supported plateforms.

      A skeleton project is on its way to ease the work.

  • TODO: expand list

Contributing

Yes please.

Licence

Apache licence 2.0

About

rotonde makes IoT development accessible to every developers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published