Ship IoT with oneMPOWER, BeagleBoard, and Texas Instruments




Ship IoT with oneMPOWER, BeagleBoard, and Texas Instruments

Ship IoT with oneMPOWER, BeagleBoard, and Texas Instruments

November 4, 2015 / IoT, wot.io, texas instruments, beagleboard, oneMPOWER / Posted By: wotio team

In this post I connect Texas Instruments Sensortags to oneMPOWER™, a M2M/IoT device management platform and implementation of the oneM2M specification, developed by the OneM2M standards organization.

wot.io IoT middleware for the connected Enterprise

Here I build on previous work (part 1, part 2) done with TI Sensortags and the Beaglebone Black from beagleboard.org, to demonstrate how easy it is to combine data services in an IoT solution using wot.io.

As you will recall from those previous posts, I used the wot.io data service exchange™ to employ DeviceHive as a device management platform data service from which I routed device notifications through some transformation logic in scriptr; and on to a Nest thermostat integration in bip.io and monitoring & metering stripchart in Circonus.

While DeviceHive is an excellent, open-source option for device management, wot.io data service exchange is about choice and IoT platform interoperability.

Today we're going to demonstrate how to use an alternative device management platform with the wot.io data service exchange middleware and the oneMPOWER™ device management platform as a wot.io data service. The loose coupling of wot.io's routing architecture and data service adapters keep everything else working seamlessly, resulting in a powerful, composable IoT/M2M solution. While not demonstrated in this post, both DeviceHive and oneMPOWER could be deployed to work together in the wot.io data service exchange.

oneM2M & oneMPOWER

oneM2M represents an extensive set of entities and protocol bindings, designed to tackle complex device management and connectivity challenges in the M2M and IoT sector. Naturally, a full treatment of how the oneM2M system works is beyond the scope of this article, and I refer you to the oneM2M specifications if you want to learn more. For this demo, you'll want to refer to these in particular:

Additionally, you will soon find public code samples in github: [currently private for review]

One of the tools that InterDigital makes available to oneM2M developers is a client application designed to view the resource hierarchy in a given oneMPOWER system. We'll use it here to visualize state changes as we interact with the oneM2M HTTP bindings. At the top is a reference diagram of oneM2M entities, helpful to have at your fingertips. You can see events as they happen in the console window on top, and at the bottom is the resource viewer. Keep an eye there for resources to appear as they are created.

InterDigital's Resource Tree Viewer

Note, the tool header lists MN-CSE, for a Middle Node, but we're working with an IN-CSE, an Infrastructure Node. These oneM2M designations are actually very similar—differentiated by their configuration to correspond to their roles in a large-scale oneM2M deployment. Don't worry about it for now, just watch the resource tree!

Application Entity Setup

For this demonstration, we will first create an Application Entity (AE) by hand, in the Common Services Entity (CSE) instantiated by the oneMPOWER server. In a full system, the devices or gateways would not typically be responsible for defining the full resource tree, so here we use curl commands against the oneMPOWER HTTP REST interface endpoints. The message is sent as XML in the body of an HTTP POST, but per the specs you can use other encodings like JSON, too.

Note that all parts of the calls are important, with critical data represented in the headers, path, and body!

curl -i -X POST -H "X-M2M-RI: xyz1" -H "X-M2M-Origin: http://abc:0000/def" -H "Content-Type: application/vnd.onem2m-res+xml; ty=2" -H "X-M2M-NM: curlCommandApp_00" -d @payloadAe.xml "http://$IPADDRESS:$PORT/$CSE"

HTTP/1.1 201 Created  
Content-Type: application/vnd.onem2m-res+xml  
X-M2M-RI: xyz1  
X-M2M-RSC: 2001  
Content-Location: /CSEBase_01/def  
Content-Length: 367

<?xml version="1.0"?>  
<m2m:ae xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-ae-v1_0_0.xsd" rn="curlCommandApp_00"><ty>2</ty><ri>def</ri><pi>CSEBase_01</pi><ct>20151030T221330</ct><lt>20151030T221330</lt><et>20151103T093330</et><aei>def</aei></m2m:ae>  

The body of the POST contains this XML data, including the application ID for the AE:

<?xml version="1.0"?>  
<m2m:ae xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-ae-v1_0_0.xsd" rn="ae">  
    <api>myAppId</api>
    <rr>false</rr>
</m2m:ae>  

Verifying the AE

Next we'll perform a simple check to make sure that the Application Entity was properly configured in the CSE. We expect to get a reply showing what we configured for the AE, and no errors.

curl -i -X GET -H "X-M2M-RI: xyz1" -H "X-M2M-Origin: http://abc:0000/def" "http://$IPADDRESS:$PORT/$CSE/curlCommandApp_00"

HTTP/1.1 200 Content  
Content-Type: application/vnd.onem2m-res+xml  
X-M2M-RI: xyz1  
X-M2M-RSC: 2000  
Content-Length: 399

<?xml version="1.0"?>  
<m2m:ae xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-ae-v1_0_0.xsd" rn="curlCommandApp_00"><ty>2</ty><ri>def</ri><pi>CSEBase_01</pi><ct>20151030T221330</ct><lt>20151030T221330</lt><et>20151103T093330</et><api>myAppId</api><aei>def</aei><rr>false</rr></m2m:ae>  

And you can see above, the ID myAppId is there! It worked! We can also see it appear in the resource tree viewer, here shown as the green box labeled "def" (a "foo" name drawn from the create call above):

Create a Container

In order to store content in a CSE, you must first create a Container entity. This is just a named bucket into which your content instances will go. Here's the call to set up a container named curlCommandContainer_00. The XML payload is more or less empty as the name implies, as we are not setting any extended attributes here.

curl -i -X POST -H "X-M2M-RI: xyz2" -H "X-M2M-Origin: http://abc:0000/$CSE/def" -H "Content-Type: application/vnd.onem2m-res+xml; ty=3" -H "X-M2M-NM: curlCommandContainer_00" -d @payloadContainerEmpty.xml "http://$IPADDRESS:$PORT/$CSE/curlCommandApp_00"

HTTP/1.1 201 Created  
Content-Type: application/vnd.onem2m-res+xml  
X-M2M-RI: xyz2  
X-M2M-RSC: 2001  
Content-Location: /CSEBase_01/def/cnt_20151030T221435_0  
Content-Length: 407

<?xml version="1.0"?>  
<m2m:cnt xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-cnt-v1_0_0.xsd" rn="curlCommandContainer_00"><ty>3</ty><ri>cnt_20151030T221435_0</ri><pi>def</pi><ct>20151030T221435</ct><lt>20151030T221435</lt><et>20151103T093435</et><st>0</st><cni>0</cni><cbs>0</cbs></m2m:cnt>  

And again, the viewer shows our container created successfully, in red. It's labeled by the resource identifier (also returned in the XML response we see above), and not by the resource name that we provided. (If you hover over the block you can verify the extra info is correct.)

Create a Content Instance

Now we're ready to get to the fun stuff, sending actual data from our devices! Before we go over to the device script, we'll run one more test to make sure we can create a Content Instance by hand.

Of note here is that each Content Instance needs a unique identifier. Here you can see its name specified by the request header X-M2M-NM: curlCommandContentInstance_00. If you run the same command with the same name, it will fail, as the content instance already exists. This makes sure you can't accidentally erase important data.

curl -i -X POST -H "X-M2M-RI: xyz4" -H "X-M2M-Origin: http://abc:0000/$CSE/def/cnt_20151030T221435_0" -H "Content-Type: application/vnd.onem2m-res+xml; ty=4" -H "X-M2M-NM: curlCommandContentInstance_00" -d @payloadContentInstance.xml "http://$IPADDRESS:$PORT/$CSE/curlCommandApp_00/curlCommandContainer_00"

HTTP/1.1 201 Created  
Content-Type: application/vnd.onem2m-res+xml  
X-M2M-RI: xyz4  
X-M2M-RSC: 2001  
Content-Location: /CSEBase_01/def/cnt_20151030T221435_0/cin_20151030T221557_1  
Content-Length: 417

<?xml version="1.0"?>  
<m2m:cin xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-cin-v1_0_0.xsd" rn="curlCommandContentInstance_00"><ty>4</ty><ri>cin_20151030T221557_1</ri><pi>cnt_20151030T221435_0</pi><ct>20151030T221557</ct><lt>20151030T221557</lt><et>20151103T093557</et><st>1</st><cs>2</cs></m2m:cin>  

This is the content we sent in the body of the request, again as XML. You can see the data field in the con element, which is the integer 22.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
<m2m:cin xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-cin-v1_0_0.xsd" rn="cin">  
    <cnf>text/plain:0</cnf>
    <con>22</con>
</m2m:cin>  

And our content instance appears in the viewer as well, in the orange block:

Content Instance

And you can see the details in a pop-up. Notice the parentID, and that it matches the container's ID from above. You can also see the data we sent at the bottom, the value 22:

Content Instance Details

Send Device Data

Running on the BeagleBoard device, we have a small Python script that communicates with the oneM2M HTTP REST interface to send periodic telemetry data to the oneMPOWER instance, and ultimately on to the wot.io bus via the wot.io oneMPOWER adapter. First, the header, where we import some libs and set our configuration: the CSE name, app name, and container name must match what's been configured in the oneMPOWER instance.

#!/usr/bin/env python

import time  
import httplib  
import os

command_temp = "python ./sensortag.py 78:A5:04:8C:15:71 -Z -n 1"  
hostname = "23.253.204.195"  
port = 7000  
csename = "CSE01"  
appname = "curlCommandApp_00"  
container = "curlCommandContainer_00"  

Next, we set up some simple helper functions to

  • read the sensor data from the TI SensorTags connecting to our device via Bluetooth (see previous post for details),
  • compose a Content Instance XML message, and
  • send it to the HTTP endpoint.

Finally, we loop and sleep to generate time-series data. Simple!

def readsensor(cmd):  
    return float(os.popen(cmd).read())

def onem2m_cin_body(value):  
    message = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<m2m:cin xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-cin-v1_0_0.xsd" rn="cin">  
<cnf>text/plain:0</cnf>  
<con>%s</con>  
</m2m:cin>""" % value  
    return message

def send(value):  
    body = onem2m_cin_body(value)
    headers = {}
    headers['Content-Length'] = "%d" % len(body)
    headers['Content-Type'] = "application/vnd.onem2m-res+xml; ty=4"
    headers['X-M2M-NM'] = "my_ci_id_%s" % time.time()
    headers['X-M2M-RI'] = "xyz1"
    headers['X-M2M-Origin'] = "http://abc:0000/def"
    path = "/%s/%s/%s" % (csename, appname, container)
    con = httplib.HTTPConnection(hostname, port)
    con.request("POST", path, body, headers)
    res = con.getresponse()
    print res.status, res.reason, res.read()
    con.close

while True:  
    print "Reading sensor\n"
    value = readsensor(command_temp)
    print "Got %f - sending\n" % value
    send(value)
    print "Sleeping...\n"
    time.sleep(30)

And now a quick example of the output as we run the above script. We see it read the SensorTag data as we have done in the past, assemble a content instance message, and send it via HTTP POST. Created content instances appear in the specified container, just as we saw above, and from there the telemetry flows back to the wot.io bus and on to other data services.

root@beaglebone:~# ./main.py  
Reading sensor

Got 44.643921 - sending

201 Created <?xml version="1.0"?>  
<m2m:cin xmlns:m2m="http://www.onem2m.org/xml/protocols" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.onem2m.org/xml/protocols CDT-cin-v1_0_0.xsd" rn="my_ci_id_1446511119.68"><ty>4</ty><ri>cin_20151103T003839_9</ri><pi>cnt_20151030T221435_0</pi><ct>20151103T003839</ct><lt>20151103T003839</lt><et>20151106T115839</et><st>9</st><cs>13</cs></m2m:cin>

Sleeping...  

oneMPOWER Protocol Analyzer

It's worth noting that there's also a protocol analyzer available in the resource tree viewer, which is handy for debugging communication sequences. You'll see some of our requests represented below:

OneM2M Protocol Analyzer

Ship your IoT Solution with wot.io Data Services

As you will recall from my previous post, we have now done everything necessary to

Whew! That's a mouthful! What a relief that wot.io's loosely-coupled architecture supports the DRY principle so that we only had to modify the third bullet. The rest of that complex data flow just continued to work just like before!

From data in motion to data at rest, and with an ever-growing selection of data service partners, wot.io has you covered, including enterprise-ready solutions like oneMPOWER. Ready for more? Head over to wot.io and dig in!