In early September I was fortunate to join other Microsoft colleagues from around the world in Paris for a hackfest. This hackfest took place as part of a larger working group within Microsoft focused on exploring the Internet-of-Things (IoT). The working group charter is the exploration of standards, protocols, and frameworks within the IoT space and how they can work within the Microsoft ecosystem. Those explorations in turn produce recommendations on how the technologies can integrate, best practices on how to develop using the frameworks, potential reference architectures, and other feedback and collateral (code samples, etc.) that can be used within Microsoft and provide guidance to the product teams involved in the IoT space.

For this hackfest, we focused on using the AllJoyn framework developed by the AllSeen Alliance in a number of different scenarios. AllJoyn is an open source project striving to provide a peer-to-peer infrastructure providing for discoverability and interoperability across an array of devices and platforms. My colleague Rachel Yehezkel has an overview of the AllJoyn framework here.

Those that were gathered in Paris include @joshholmes, @tomconte, @mszcool, @reichenseer, @timpark, @irjudson, @ankoduizer, @rachelyehe, @jmspring, and daniele-colonna.

So What Did We Do?

The first day of the hackfest those in attendance came up with a set of goals for the week and we broke out into teams to work on those goals. Included among those goals were:

  • Building for the AllJoyn Framework using Visual Studio 2013
  • Using the AllJoyn Thin Client on an Intel Galileo board
  • Cloud control of an AllJoyn based device

My colleague Mario Szpuszta talks about getting things going with Visual Studio 2013 here. Thomas Conte and Daniele Colonna explore the thin client and Intel Galileo here. The team I was part of took on the cloud control of an AllJoyn based device.

For cloud control of an AllJoyn based device, we decided upon the following general stack:

  • Embedded/constrained hardwared running as both an AllJoyn client and bus
  • Windows application that provides command and control of the AllJoyn client based upon input from the cloud controller
  • A cloud controller that exposes commands that can be sent to the device

For the Windows client -> Cloud connection, we decided upon using @timpark‘s nitrogen.io project. Ideally, the Window’s component would be an AllJoyn -> Nitrogen.io gateway, but that is still in the process of being developed.

Our application was a simple LED toggle with the following commands:

  • on
  • off
  • blink with duration (duration was a single value for both time on and time off)

The administrative app within Nitrogen.io exposed the UI to control the LED as well as generated the Nitrogren.io messages to be delivered to the embedded device. The Windows application picked up the messages and translated those into AllJoyn messages to send to the embedded device. The embedded device received the messages and acted upon them.

For the embedded device, we chose the BeagleBone Black which is a low cost, Linux-based development board.

Getting Started with AllJoyn and the BeagleBone Black

In order to build an AllJoyn application for the BeagleBone Black, we first need to set up a development environment in order to build the applications for the BeagleBone Black. It should be noted that the BeagleBone Black does come with a full installation of Linux on it (typically Angstrom or Debian), but due to limited space and variations in installed platforms, we decided to opt for cross compiling on an Ubuntu based system (in this case, a VM, but that matters little)..

AllJoyn provides a guide to getting started on Linux. For general development, do consult that guide. For purposes of cross compiling, the steps below assume a clean install of Ubuntu Server 14.04. The steps also assume we are building

The first step is to install the required tools for cross compilation, as well as packages needed for building the AllJoyn framework itself. The target platform for cross compiling to the BeagleBone Black is “arm” and the application binary interface is “gnueabi”. In the Ubuntu package repository, the relevant packages are of the form <name>-arm-linux-gnueabi. For building AllJoyn, “scons” and “uncrustify” are two packages that are required. The AllJoyn documentation specifies that it requries version 0.57 of “uncrustify”, the package for Ubuntu 14.04 is version 0.59 and works fine.

To install the relevant packages, do the following:

$ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf pkg-config-arm-linux-gnueabihf make
$ sudo apt-get install scons uncrustify

The next requirement for AllJoyn is OpenSSL. Like AllJoyn, OpenSSL needs to be cross compiled for the BeagleBone Black. Additionally, it needs to be installed to a location we can later reference. For purposes of this tutorial, we will assume the base directory is “/home/alljoyn/deps/openssl”. To build and install the cross platform version of OpenSSL, the following steps are required:

$ mkdir -p /tmp/openssl
$ mkdir -p /home/alljoyn/deps/openssl
$ cd /tmp/openssl
$ wget http://www.openssl.org/source/openssl-1.0.1i.tar.gz
$ tar -zxvf openssl-1.0.1i.tar.gz
$ cd openssl-1.0.1i
$ export PKG_CONFIG=/usr/bin/arm-linux-gnueabi-pkg-config
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabi-
$ ./Configure --openssldir=/home/alljoyn/deps/openssl shared os/compiler:${CROSS_COMPILE}
$ make CC=${CROSS_COMPILE}gcc RANLIB=${CROSS_COMPILE}ranlib
$ make install

As of version 14.06 of the AllJoyn framework, the process of building for “linux” on “arm” has some hard coded path/filename requirements. Additionally, the build system is missing support for cross compiling when the host and destination are both linux systems.

Before we address the build system issue, we need to document the steps to get the AllJoyn source and set up the required hard coded paths and environment variables. For this blog we will assume the AllJoyn work is within the base directory of “/home/alljoyn/src”.

$ mkdir -p /home/alljoyn/src
$ cd /home/alljoyn/src
$ wget https://allseenalliance.org/releases/alljoyn/14.06.00/alljoyn-suite-14.06.00a-src.tar.gz
$ tar -zxvf alljoyn-suite-14.06.00a-src.tar.gz
$ cd alljoyn-suite-14.06.00a-src
  $ export AJ_ROOT=`pwd`
$ mkdir -p /home/alljoyn/oe_base/bin
$ ln -s /usr/bin/arm-linux-gnueabi-g++ /home/alljoyn/oe_base/bin/arm-angstrom-linux-gnueabi-g++
$ ln -s /usr/bin/arm-linux-gnueabi-gcc /home/alljoyn/oe_base/bin/arm-angstrom-linux-gnueabi-gcc
$ export OE_BASE=/home/alljoyn/oe_base
$ export OPENSSL_ROOT=/home/alljoyn/deps/openssl
$ cd core/alljoyn
$ scons CPU=arm OS=linux BINDINGS=c,cpp WS=off

You will note the setting of the “OPENSSL_ROOT” environment variable. The build system for version 14.06 of the AllJoyn framework needs a patch in order to locate the cross compiled version of OpenSSL. The necessary patch which must be applied prior to executing “scons” and is applied to the file alljoyn-suite-14.06.00a-src/core/alljoyn/common/SConscript. The patch:

diff --git a/alljoyn-suite-14.06.00a-src/core/alljoyn/common/SConscript b/alljoyn-suite-14.06.00a-src/core/alljoyn/common/SConscript
index b4d354c..3b2aed1 100644
--- a/alljoyn-suite-14.06.00a-src/core/alljoyn/common/SConscript
+++ b/alljoyn-suite-14.06.00a-src/core/alljoyn/common/SConscript
@@ -69,6 +69,18 @@ elif env['OS_GROUP'] == 'winrt':

 elif env['OS'] in ['linux', 'openwrt']:
     env.AppendUnique(LIBS =['rt', 'stdc++', 'pthread', 'crypto', 'ssl', 'm'])
+    if env['CPU'] in ['arm', 'armv7', 'armv7s']:
+        vars = Variables()
+        vars.Add(PathVariable('OPENSSL_ROOT', 'Base OpenSSL directory', os.environ.get('OPENSSL_ROOT')))
+        vars.Update(env)
+        Help(vars.GenerateHelpText(env))
+        if '' == env.subst('$OPENSSL_ROOT'):
+            # Must specify OPENSSL_ROOT for linux, arm
+            print 'Must specify OPENSSL_ROOT when building for OS=linux, CPU=arm'
+            if not GetOption('help'):
+                Exit(1)
+        env.Append(CPPPATH = ['$OPENSSL_ROOT/include'])
+        env.Append(LIBPATH = ['$OPENSSL_ROOT/lib'])

When the build completes successfully, the output is located in the directory /home/alljoyn/src/alljoyn-suite-14.06.00a-src/core/alljoyn/build/linux/arm/debug/dist/.

Now we can write an application against it.

An AllJoyn Application

For the LED application, we started with the files basic_c_service.c and basic_c_client.c which are found in alljoyn-suite-14.06.00-src/core/alljoyn/alljoyn_c/samples/basic. The sample includes most of the boiler plate code needed to create an AllJoyn service. For our application, we need to define our interfaces and implement them. These will replace the “concatenation” method in the sample above.

Taking a step back, the steps for exposing a command on the AllJoyn bus for the service are as follows:

  1. Create an interface on the message bus. The relevant AllJoyn framework method call: alljoyn_busattachment_createinterface.

    alljoyn_busattachment_createinterface(g_msgBus, INTERFACE_NAME, &interfaceHandle);
    
  2. Add a member to that interface of the type “ALLJOYN_MESSAGE_METHOD_CALL” to the interface. The relevant AllJoyn framework method call: alljoyn_interfacedescription_addmember.

    alljoyn_interfacedescription_addmember(interfaceHandle, ALLJOYN_MESSAGE_METHOD_CALL,
                                           "flash", "du", "du",
                                           "brightnessIn,durationIn,brightnessOut,durationOut,
                                           0);
    

    Note that fourth and fifth arguments are the string “du”, AllJoyn uses this short hand to define the types of arguments the method takes and returns. The process for defining method signatures can be found here.

  3. Create the bus object and add interface created previously to it. The relevant method calls are alljoyn_busobject_create, alljoyn_busattachment_getinterface, and alljoyn_busobject_addinterface.

    alljoyn_busobject busObj;
    alljoyn_interfacedescription interfaceHandle;
    busObj = interfaceHandle(OBJECT_PATH, QCC_FALSE, &busObjCbs, NULL);
    interfaceHandle = alljoyn_busattachment_getinterface(g_msgBus, INTERFACE_NAME);
    alljoyn_busobject_addinterface(busObj, interfaceHandle);
    
  4. Map a handle for the method created in (2) to the actual handler function. To retrieve a handle to the method in the interface, use the functio alljoyn_interfacedescription_getmember

    void flash_method(alljoyn_busobject bus, const alljoyn_interfacedescription_member* member, alljoyn_message msg)
    {
        ...
    }
    
    ...
    
    alljoyn_interfacedescription_member flash_member;
    alljoyn_busobject_methodentry methodEntries[] = {
      { &flash_member, flash_method },
      ...
    };
    ...
    alljoyn_interfacedescription_getmember(interfaceHandle, "flash", &flash_member);
    
  5. Add the interface methods to the bus object using the command alljoyn_busobject_addmethodhandlers.

    alljoyn_busobject_addmethodhandlers(busObj, methodEntries, sizeof(methodEntries) / sizeof(methodEntries[0]));
    

Note there is certainly more to a complete application, as you can see in the files below. However, the above gives the nuts and bolts to define an interface, the method for that interface and expose them on the AllJoyn bus.

Another piece of the puzzle towards a working method is the actual handler that is called when a remote method call is made to the service. The general structure for a method handler is as follows:

void flash_method(alljoyn_busobject bus, const alljoyn_interfacedescription_member* member, alljoyn_message msg)
{
    QStatus status;
    alljoyn_msgarg outArg;
    double brightness;
    uint32_t duration;

    /* retrieve message arguments */
    alljoyn_msgarg_get(alljoyn_message_getarg(msg, 0), "d", &brightness);
    alljoyn_msgarg_get(alljoyn_message_getarg(msg, 1), "u", &duration);

    /* do some stuff here */
    ...

    /* generate the response */
    size_t numArgs = 2;
    outArg = alljoyn_msgarg_array_create(numArgs);
    status = alljoyn_msgarg_array_set(outArg, &numArgs, "du", brightness, duration);

    /* send the response */
    alljoyn_busobject_methodreply_args(bus, msg, outArg, numArgs);

    /* clean up */
    alljoyn_msgarg_destroy(outArg);
}

The most interesting pieces above are retrieving arguments from the incoming message and then forming and sending the response. The calls to alljoyn_msgarg_get need to match the signature you defined for your methods. Above, we defined the flash command as taking a double and a uint32_t using the signature “du”, as you see above, we retrieve the values using the same signature.

To return the result, you create the return argument and make a call to alljoyn_busobject_methodreply_args. If you look at the example basic_c_service.c you will note that it uses the following command to create the response argument:

alljoyn_msgarg_create_and_set("s", result);

However, in the LED example, we need to create the return argument as follows:

/* generate the response */
size_t numArgs = 2;
outArg = alljoyn_msgarg_array_create(numArgs);
status = alljoyn_msgarg_array_set(outArg, &numArgs, "du", brightness, duration);

The reason for this, there appears to be a limitation in the command alljoyn_msgarg_create_and_set. If you look at the function declaration:

alljoyn_msgarg alljoyn_msgarg_create_and_set(const char* signature, ...)

The expectation would be that you should be able to make a call like:

outArg = alljoyn_msgarg alljoyn_msgarg_create_and_set("du", brightness, duration);

If you look at the actual code in alljoyn-suite-14.06.00-src/core/alljoyn/alljoyn_c/src/MsgArg.cc, you will see that the function is written in a way that implies such:

alljoyn_msgarg alljoyn_msgarg_create_and_set(const char* signature, ...)
{
    QCC_DbgTrace(("%s", __FUNCTION__));
    ajn::MsgArgC* arg = new ajn::MsgArgC[1];
    arg->typeId = ajn::ALLJOYN_INVALID;
    va_list argp;
    va_start(argp, signature);
    QStatus status = ER_OK;

    ((ajn::MsgArgC*)arg)->Clear();
    size_t sigLen = (signature ? strlen(signature) : 0);
    if ((sigLen < 1) || (sigLen > 255)) {
        status = ER_BUS_BAD_SIGNATURE;
    } else {
        status = ((ajn::MsgArgC*)arg)->VBuildArgsC(signature, sigLen, ((ajn::MsgArgC*)arg), 1, &argp);
        if ((status == ER_OK) && (*signature != 0)) {
            status = ER_BUS_NOT_A_COMPLETE_TYPE;
        }
    }
    va_end(argp);
    return (alljoyn_msgarg)arg;
}

However, in practice, this was not the case. For multiple return values, the array approach is required. The “bug” shows itself in status resulting in ER_BUS_NOT_A_COMPLETE_TYPE and only one entry in the signature is parsed.

Once the code is written, the next step is to compile it. Ideally, one would set up a make file for building (or a SCONS file). In order to compile the file led_service.c for the BeagleBone, the following command line will accomplish that (assuming the you are in the directory where the LED service is being developed):

arm-linux-gnueabi-gcc -D QCC_OS_GROUP_POSIX \
 -I $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/inc/ \
 -I $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/c/inc \
 -o led_service \
 led_service.c \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/c/lib/liballjoyn_c.a \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/lib/libajrouter.a \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/lib/liballjoyn.a \
 /home/alljoyn/deps/openssl/inst/lib/libcrypto.a \
 -lstdc++ -lm -lpthread -lrt

Note, the above is building the service linking against static libraries. This just makes it easier to transfer the binary to the BeagleBone Black. Shared libraries are also available in the AllJoyn framework.

Running Service on the BeagleBone

In order to run the service on the BeagleBone, we need to transfer two executables to the device. The first is led_service which we previously generated. The second is the Alljoyn binaries and libraries we previously built. For the Alljoyn binaries, we will package them up, transport them to the BeagleBone and expand them into /usr/local/allyjoyn.

On the development machine (assuming you are in the directory you built the led_service file):

# tar -zcf ~/alljoyn_libs_binaries_and_app.tgz $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/{c,cpp} led_service
# scp ~/alljoyn_libs_binaries_and_app.tgz root@beaglebone.local:.

On the BeagleBone (assuming you are in the root home directory):

# mkdir tmp
# cd tmp
# tar -xvf ../alljoyn_libs_binaries_and_app.tgz
# mkdir /usr/local/alljoyn
# mv build/linux/arm/debug/dist/c build/linux/arm/debug/dist/cpp /usr/local/alljoyn/

To get the service going, we need to add the AllJoyn libraries into the LD_LIBRARY_PATH. While the applications are compiled statically, the alljoyn-daemon application, which is required is not.

# export LD_LIBRARY_PATH=/usr/local/alljoyn/cpp/lib:/usr/local/alljoyn/c/lib

Start the alljoyn-daemon:

# /usr/local/alljoyn/cpp/bin/alljoyn-daemon &

Start the led_service (again assuming we are in the root home directory):

# ./led_service AllJoyn Library version: v14.06.00a AllJoyn Library build info: AllJoyn Library v14.06.00a (Built Tue Jan 20 23:21:30 UTC 2015 by root - Git: alljoyn branch: ‘(no branch)’ tag: ‘v14.06a-rc1’ (+0 changes) commit ref: 9d096987205609053a9a656ebd72972ea92eff17) Interface Created. alljoyn_busattachment started. ObjectRegistered has been called alljoyn_busattachment connected to “unix:abstract=alljoyn” name_owner_changed: name=org.alljoyn.sample.ledcontroller.beagle, oldOwner=<none>, newOwner=:9DWlHpyG.2

AllJoyn Client

In order to talk with the service, there needs to be a corresponding client that can talk to the AllJoyn led_service. Before issuing commands, there are a series of steps necessary for the client to setup and connect to the AllJoyn bus and discover the led_service before remote methods can be called.

The setup steps necessary to register with and discover the service are:

# Create an attachement to the bus.

/* Create message bus */
g_msgBus = alljoyn_busattachment_create("myApp", QCC_TRUE);

# Define the interface the client is interested in and define the members the client will call.

status = alljoyn_busattachment_createinterface(g_msgBus, INTERFACE_NAME, &ledIntf);
alljoyn_interfacedescription_addmember(ledIntf, ALLJOYN_MESSAGE_METHOD_CALL, "flash", "du",  "du", "brightnessIn,frequencyIn,brightnessOut,frequencyOut", 0);
...
alljoyn_interfacedescription_activate(ledIntf);

# Start the bus attachement and connect to it

status = alljoyn_busattachment_start(g_msgBus);
status = alljoyn_busattachment_connect(g_msgBus, connectArgs);

# Create and register a bus listener.

g_busListener = alljoyn_buslistener_create(&callbacks, NULL);
alljoyn_busattachment_registerbuslistener(g_msgBus, g_busListener);
# Begin the discovery process of the service you wish to attach to (the led_service) in

this case. And wait for the join to be complete (or be interrupted).

status = alljoyn_busattachment_findadvertisedname(g_msgBus, OBJECT_NAME);
while (s_joinComplete == QCC_FALSE && g_interrupt == QCC_FALSE) {
    usleep(100 * 1000);
}

Once that is completed, for each command that is to be issued, the basic steps are:

# Create a remote object and connect it to the desired interface

alljoyn_proxybusobject remoteObj = alljoyn_proxybusobject_create(g_msgBus, OBJECT_NAME, OBJECT_PATH, s_sessionId);
const alljoyn_interfacedescription alljoynLedIntf = alljoyn_busattachment_getinterface(g_msgBus, INTERFACE_NAME);
alljoyn_proxybusobject_addinterface(remoteObj, alljoynLedIntf);
# Create the message to send corresponding the the method call you wish to invoke and

make the call

alljoyn_message reply;
alljoyn_msgarg inputs;
size_t numArgs = 2;
reply = alljoyn_message_create(g_msgBus);
inputs = alljoyn_msgarg_array_create(numArgs);
status = alljoyn_msgarg_array_set(inputs, &numArgs, "du", brightness, frequency);
status = alljoyn_proxybusobject_methodcall(*remoteObj, INTERFACE_NAME, "flash", inputs, numArgs, reply, 5000, 0);
# Process the response. In the case of the call to “flash”, the method returns brightness

and frequency.

status = alljoyn_msgarg_get(alljoyn_message_getarg(reply, 0), "d", &brightness);
status = alljoyn_msgarg_get(alljoyn_message_getarg(reply, 1), "u", &frequency);

Like the service component, you build the client by linking against the same libraries that you did for the service. This can be done on any machine that supports AllJoyn, but for simplicity, doing so for the BeagleBone, the command is as follows:

arm-linux-gnueabi-gcc -D QCC_OS_GROUP_POSIX \
 -I $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/inc/ \
 -I $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/c/inc \
 -o led_client \
 led_client.c \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/c/lib/liballjoyn_c.a \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/lib/libajrouter.a \
 $AJ_ROOT/core/alljoyn/build/linux/arm/debug/dist/cpp/lib/liballjoyn.a \
 /home/alljoyn/deps/openssl/inst/lib/libcrypto.a \
 -lstdc++ -lm -lpthread -lrt

Assuming you have started the AllJoyn daemon as well as started the led_service from above, running the led_client, would resemble something in the window where the client command is issued looking like:

# ./led_client flash 50 2000
AllJoyn Library version: v14.06.00a
AllJoyn Library build info: AllJoyn Library v14.06.00a (Built Tue Jan 20 23:21:30 UTC 2015 by root - Git: alljoyn branch: '(no branch)' tag: 'v14.06a-rc1' (+0 changes) commit ref: 9d096987205609053a9a656ebd72972ea92eff17)
Interface Created.
alljoyn_busattachment started.
alljoyn_busattachment connected to "unix:abstract=alljoyn"
alljoyn_buslistener Registered.
found_advertised_name(name=org.alljoyn.sample.ledcontroller.beagle, prefix=org.alljoyn.sample.ledcontroller.beagle)
alljoyn_busattachment_joinsession SUCCESS (Session id=2048531324)
{ "cmd": "flash", "brightness": 50.000000, "frequency": 2000 }
basic client exiting with status 0 (ER_OK)

On the BeagleBone Black you should see one of the LEDs blinking two seconds on, two seconds off.

Final Notes

There is a lot more to the AllJoyn protocol. Some AllJoyn blog posts written by some of my colleagues:

The source for the led_service.c and led_client.c can be found here.