Get Smart Things Talking with AllJoyn and Kii
Join the DZone community and get the full member experience.
Join For FreeAvoid lower-level network protocols and hardware in your Internet of Things projects with AllJoyn open source software.
As members of the AllSeen Alliance we’re excited about the AllJoyn™ framework for Internet of Things (IoT) projects. The de facto object-oriented approach, AllJoyn reduces development complexity by allowing for proximity peer to peer over various transports. It’s written in C++ at its core, provides multiple language bindings (such as Java, JavaScript, C#, ObjC) and supports complete implementations across various operating systems and chipsets.
Below are the steps to quickly get started with AllJoyn using JavaScript binding (in nodejs) to allow peers to exchange messages. You’ll also learn how to cloud-enable the project by logging received messages on Kii for later processing. In this tutorial we’re sending text messages but Kii can richer data such as music, photo, video, etc so you can be sending songs to smart speakers or photos to a smart TV with cloud-enabled storage and Analytics, for example.
AllJoyn framework overview
Before jumping into code, you should know a few AllJoyn basics. Any peer participating in an AllJoyn conversation must “talk” via a (relatively low level) bus. Think of it as the highway in which all AllJoyn messages travel. First your app needs to attach to the bus by creating a bus attachment. Through a bus a peer can participate in an AllJoyn conversation but it has to be done through a concrete interface. The interface is created in the bus with the purpose of handling shared objects and signals sent to the objects. In our example the peers will share a chat object in the bus and signals sent to it will be relayed to the other peers as messages.
Next you have to decide whether your app wants to join and use an existing service or advertise one for others to join. Assuming you want to advertise a service, give it a recognizable name. Other peers will try to locate your service using it. Then bind a port (e.g. 27) to create a session over the bus. The steps:
- Create a BusAttachment and connect to the AllJoyn framework to communicate with other AllJoyn applications
- Create an Interface in the Bus and define the Signals that it will handle
- Create a BusObject and pair it with the Interface (after starting the Bus)
- Define a SignalHandler for signals sent to the BusObject
- Register the BusObject with the Bus and connect
- If Advertising:
- Choose a unique identifier. Create a unique name
- Create a Session other applications can join
- Tell other applications you’re there by advertising the service name
- If Joining:
- Find other applications nearby by looking for a name prefix
- Define a BusListener to join a Session once a service name is found
- Join a session that has been found
- Communicate with other applications in your group
Don’t panic. We’ll go through the code needed to achieve all the necessary steps below.
Install and compile AllJoyn
Before taking AllJoyn for a spin let’s install it. You could download the source and compile it yourself (e.g. for a Raspberry Pi) but the easiest way is to use the npm package (thanks Octoblu!) and test the framework with the JavaScript binding (requires nodejs). Installation goes like this:
- Install npm and nodejs (alternative install available here)
- Install the AllJoyn npm package
- Install the node-jquery-xhr npm package (required by the Kii js SDK)
- Download the Kii js SDK and place on the same directory where you’ll run your scripts
- Test the example-host.js and example-client.js by Octublu to make sure everything works
- On Mac the scripts are located on /opt/local/lib/node_modules/alljoyn (if you installed globally with -g) and can be run with the node command
Advertise the chat service
Next take a look at our nodejs script. It can run in host mode to advertise a chat service for other peers to join. Or you can run it as a client, which will look for a service advertised by the host on the network by name, and then connect to it. This setup allows as many clients as you want but there must be only one host. The reason for using a single script for host and client is that 95% of the code is shared (see below).
Once connected all peers (host and clients) have the same behavior listening to and showing incoming messages, and allowing you to type messages for other peers to see. As an add-on all hosts and clients log incoming messages on the cloud with Kii for later analysis.
Initialize and authenticate with Kii
Once you created a Kii app at developer.kii.com, write down the app ID and key to initialize Kii:
require("node-jquery-xhr"); kii = require("./KiiSDK").create(); console.log("Initializing Kii Cloud..."); kii.Kii.initializeWithSite(appid, appkey, kii.KiiSite.US);
Once Kii is initialized a sign-in is attempted with the peer name and password passed via the command line. If this fails a first time registration is attempted against the Kii service:
console.log("Authenticating peer on Kii Cloud..."); // Authenticate the peer kii.KiiUser.authenticate(uuid, password, { // Called on successful authentication success: function(peer) { // Print some info to the log console.log("KiiCloud: Peer authenticated"); //console.log(peer); }, // Called on a failed authentication failure: function(peer, errorString) { // Print some info to the log console.log("Error authenticating peer: " + errorString); console.log("Trying peer registration..."); // Create the KiiUser object var user = kii.KiiUser.userWithUsername(uuid, password); // Register the user, defining callbacks for when the process completes user.register({ // Called on successful registration success: function(peer) { // Print some info to the log console.log("KiiCloud: Peer registered"); //console.log(peer); }, // Called on a failed registration failure: function(peer, errorString) { // Print some info to the log console.log("Error registering peer: " + errorString); process.exit(2); } }); } })
Note that the Kii API provides async method calls (with callbacks) in its methods, which makes control fairly easy and allows you to keep working on your script while a user signs in or registers.
Set up the AllJoyn bus
Some bus setup has to be done in any chat peer that wants to participate in a conversation using AllJoyn:
console.log("Loading alljoyn bus..."); var sessionId = 0; var portNumber = 27; var serviceName = "org.alljoyn.bus.samples.chat.test"; var bus = alljoyn.BusAttachment("chat"); var inter = alljoyn.InterfaceDescription(); // [...] bus listener setup goes here console.log("CreateInterfaceInBus " + bus.createInterface(serviceName, inter)); console.log("AddingSignal " + inter.addSignal("Chat", "s", "msg")); console.log("Registering bus listener..."); bus.registerBusListener(busListener); console.log("StartingBus " + bus.start()); console.log("Creating chat service object"); var chatObject = alljoyn.BusObject("/chatService"); console.log("AddInterfaceToObject " + chatObject.addInterface(inter)); // [...] signal handler setup go here console.log("RegisterBusObject " + bus.registerBusObject(chatObject)); console.log("ConnectBus " + bus.connect());
A chat object is used as a common structure between peers to receive signals. The signal handler will interpret signals as messages.
Advertise the service on the host
After hooking up to an AllJoyn bus the host advertises the service name, combining a session, port and port listener callback:
console.log("BusRequestName " + bus.requestName(serviceName)); console.log("BusBindSessionPortWithPortListener " + bus.bindSessionPort(27, portListener)); console.log("BusAdvertiseName " + bus.advertiseName(serviceName));
The host will effectively listen on port 27 for client connections and execute the port listener callback on each connection.
Find the service on the client
The client goes through an identical bus setup as the host. Instead of advertising a service it looks for one (given the same service name):
console.log("BusFindAdvertisedName " + bus.findAdvertisedName(serviceName));
For the client the bus listener callback is key because it defines what to do when a service name is found. In this case, when the service is found, we tell the client to join a session with the host:
var busListener = alljoyn.BusListener( function(name){ console.log("BusListener: FoundAdvertisedBusName", name); sessionId = bus.joinSession(name, portNumber, 0); console.log("BusListener: JoinedSession " + sessionId); }, function(name){ console.log("BusListener: LostAdvertisedBusName", name); }, function(name){ console.log("BusListener: BusNameOwnerChanged", name); } );
Send, receive and log messages
Sending and receiving messages is done the same way for host and client. First a shared bus object is created on the bus and each peer subscribes to signals sent to it via a signal handler.
In order to send messages the peer takes input from stdin and then sends a signal to the shared chat object:
var stdin = process.stdin; // resume stdin in the parent process (node app won't quit all by itself // unless an error or process.exit() happens) stdin.resume(); // avoid binary stdin.setEncoding( 'utf8' ); // on any data into stdin stdin.on( 'data', function( key ){ // ctrl-c ( end of text ) if ( key === '\u0003' ) { process.exit(); } // write the key to stdout all normal like process.stdout.write( ":me " + key); chatObject.signal(null, sessionId, inter, "Chat", key.replace(/(\r\n|\n|\r)/gm,"")); });
In order to receive a message each peer has a signal handler that interprets the signal as an incoming message, sends it to the console and does the logging on Kii:
console.log("RegisterSignalHandler " + bus.registerSignalHandler(chatObject, function(msg, info){ // console.log("Signal received: ", msg, info); console.log(info["sender"], msg["0"]); //log received message on Kii Cloud //create an application scope bucket var appBucket = kii.Kii.bucketWithName(bucketName); // Create the object with key/value pairs var obj = appBucket.createObject(); obj.set("message", msg["0"]); obj.set("sender", info["sender"]); obj.set("session_id", info["session_id"]); obj.set("timestamp", info["timestamp"]); obj.set("member_name", info["member_name"]); obj.set("object_path", info["object_path"]); obj.set("signature", info["signature"]); // Save the object obj.save({ success: function(obj) { //console.log("Msg logged"); //console.log(obj); }, failure: function(obj, errorString) { console.log("Error logging msg: " + errorString); } }); }, inter, "Chat"));
Please note: In order to store Kii data you’ll need to create a KiiObject from an application scope bucket and set all the details of the incoming message as in a key/value store. After that you can save the object on Kii with an async call and use the Kii Data Browser available on developer.kii.com to see stored messages:
That’s about it. Keep in mind that peers interpret messages as text to display on screen but these exchanged messages could be more complex structures, commands, etc. suitable for all kinds of IoT scenarios. Even binary formats could be exchanged and saved on Kii as files.And of course, besides storing data on Kii Cloud you can retrieve it via a powerful query system as easily.
Wrap up
Getting started with AllJoyn is easy thanks to the npm package with js binding. Since Kii provides JavaScript SDK integration with the binding, you can have an AllJoyn enabled chat service with cloud logging up and running in no time. Keep in mind that peers could easily be IoT devices interpreting the messages as commands to do something meaningful or exchanging richer data such as streaming music (more on that coming to the Kii Blog soon).
Full source code for both the host and client can be found here. Usage is straightforward:
> node peer.js [MODE] [YOUR_APP_ID] [YOUR_APP_KEY] [USERNAME] [PASSWORD]
MODE is either “host” or “client”. Run in host mode first, then open another shell and run the client mode (if you get errors about the address being in use try from another machine on the network since the embedded AllJoyn daemon is then using the same address). Note that you should run only one host but can have as many clients as you want. The parameters YOUR_APP_ID and YOUR_APP_KEY can be obtained by creating an app at developer.kii.com. And you can freely set your username and password, which will be created the first time if credentials don’t exist on Kii. We recommend using a different username for each running script (otherwise all scripts no matter if they are hosts or clients will do the logging as the same Kii user).
Happy coding
Opinions expressed by DZone contributors are their own.
Comments