<p>In this post I connect <a href="http://www.ti.com/ww/en/wireless_connectivity/sensortag2015/index.html?INTC=SensorTag">Texas Instruments Sensortags</a> to <a href="http://www.interdigital.com/solutions/onempower-platform">oneMPOWER™</a>, a M2M/IoT device management platform and implementation of the <a href="http://onem2m.org/technical/published-documents">oneM2M</a> specification, developed by the <a href="http://onem2m.org">OneM2M standards organization</a>.</p>
<h1 id="wotioiotmiddlewarefortheconnectedenterprise">wot.io IoT middleware for the connected Enterprise</h1>
<p>Here I build on previous work (<a href="/ship-iot-with-beaglebone-black-ti-sensortags-and-devicehive">part 1</a>, <a href="/ship-iot-with-beaglebone-black-ti-sensortags-and-devicehive-part-2">part 2</a>) done with <a href="http://www.ti.com/ww/en/wireless_connectivity/sensortag2015/index.html?INTC=SensorTag">TI Sensortags</a> and the <a href="http://beagleboard.org/black">Beaglebone Black</a> from <a href="http://beagleboard.org">beagleboard.org</a>, to demonstrate how easy it is to combine data services in an IoT solution using <a href="http://wot.io">wot.io</a>.</p>
<p>As you will recall from those previous posts, I used the <a href="http://www.wot.io/">wot.io</a> data service exchange™ to employ <a href="http://devicehive.com/">DeviceHive</a> as a device management platform data service from which I routed device notifications through some transformation logic in <a href="http://scriptr.io">scriptr;</a> and on to a <a href="http://nest.com">Nest</a> thermostat integration in <a href="http://bip.io">bip.io</a> and monitoring & metering stripchart in <a href="http://circonus.com">Circonus</a>.</p>
<p>While DeviceHive is an excellent, open-source option for device management, wot.io data service exchange is about choice and IoT platform interoperability.</p>
<p>Today we're going to demonstrate how to use an alternative device management platform with the wot.io data service exchange middleware and the <a href="http://www.interdigital.com/solutions/onempower-platform">oneMPOWER™</a> 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.</p>
<h1 id="onem2monempower">oneM2M & oneMPOWER</h1>
<p>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 <a href="http://onem2m.org/technical/published-documents">the oneM2M specifications</a> if you want to learn more. For this demo, you'll want to refer to these in particular:</p>
<ul>
<li><a href="http://onem2m.org/images/files/deliverables/TS-0001-Functional_Architecture-V1_6_1.pdf">TS 0001 Functional Architecture</a></li>
<li><a href="http://onem2m.org/images/files/deliverables/TS-0009-HTTP_Protocol_Binding-V1_0_1.pdf">TS 0009 HTTP Protocol Binding</a></li>
</ul>
<p>Additionally, you will soon find public code samples in github: [currently private for review]</p>
<ul>
<li><a href="https://github.com/wotio/shipiot-ti-sensortag-onem2m">https://github.com/wotio/shipiot-ti-sensortag-onem2m</a></li>
</ul>
<p>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.</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_1.png" alt="InterDigital's Resource Tree Viewer" /></p>
<p><em>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!</em></p>
<h3 id="applicationentitysetup">Application Entity Setup</h3>
<p>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 <code>curl</code> 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.</p>
<p><em>Note that all parts of the calls are important, with critical data represented in the headers, path, and body!</em></p>
<pre><code class="language-prettyprint lang-bash">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>
</code></pre>
<p>The body of the POST contains this XML data, including the application ID for the AE:</p>
<pre><code class="language-prettyprint lang-xml"><?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>
</code></pre>
<h3 id="verifyingtheae">Verifying the AE</h3>
<p>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.</p>
<pre><code class="language-prettyprint lang-bash">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>
</code></pre>
<p>And you can see above, the ID <code>myAppId</code> is there! It worked! We can also see it appear in the resource tree viewer, here shown as the green box labeled <code>"def"</code> (a <code>"foo"</code> name drawn from the create call above):</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_2.png" alt="" /></p>
<h3 id="createacontainer">Create a Container</h3>
<p>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 <code>curlCommandContainer_00</code>. The XML payload is more or less empty as the name implies, as we are not setting any extended attributes here.</p>
<pre><code class="language-prettyprint lang-bash">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>
</code></pre>
<p>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.)</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_3.png" alt="" /></p>
<h3 id="createacontentinstance">Create a Content Instance</h3>
<p>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.</p>
<p>Of note here is that each Content Instance needs a unique identifier. Here you can see its name specified by the request header <code>X-M2M-NM: curlCommandContentInstance_00</code>. 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.</p>
<pre><code class="language-prettyprint lang-bash">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>
</code></pre>
<p>This is the content we sent in the body of the request, again as XML. You can see the data field in the <code>con</code> element, which is the integer 22.</p>
<pre><code class="language-prettyprint lang-xml"><?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>
</code></pre>
<p>And our content instance appears in the viewer as well, in the orange block:</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_4.png" alt="Content Instance" /></p>
<p>And you can see the details in a pop-up. Notice the <code>parentID</code>, and that it matches the container's ID from above. You can also see the data we sent at the bottom, the value 22:</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_cin_data.png" alt="Content Instance Details" /></p>
<h1 id="senddevicedata">Send Device Data</h1>
<p>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.</p>
<pre><code class="language-prettyprint lang-python">#!/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"
</code></pre>
<p>Next, we set up some simple helper functions to</p>
<ul>
<li>read the sensor data from the TI SensorTags connecting to our device via Bluetooth (see <a href="/ship-iot-with-beaglebone-black-ti-sensortags-and-devicehive">previous post</a> for details),</li>
<li>compose a Content Instance XML message, and</li>
<li>send it to the HTTP endpoint.</li>
</ul>
<p>Finally, we loop and sleep to generate time-series data. Simple!</p>
<pre><code class="language-prettyprint lang-python">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)
</code></pre>
<p>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.</p>
<pre><code class="language-prettyprint lang-bash">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...
</code></pre>
<h1 id="onempowerprotocolanalyzer">oneMPOWER Protocol Analyzer</h1>
<p>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:</p>
<p><img src="http://idfiles.leveelabs.com/55bd0288af0b0930ba599bd0c4b7ca38/resources/img_new/labs_wot_io/debug_onem2m_protocol_analyzer.png" alt="OneM2M Protocol Analyzer" /></p>
<h1 id="shipyouriotsolutionwithwotiodataservices">Ship your IoT Solution with wot.io Data Services</h1>
<p>As you will recall from my <a href="http://labs.wot.io/ship-iot-with-beaglebone-black-ti-sensortags-and-devicehive-part-2/">previous post</a>, we have now done everything necessary to</p>
<ul>
<li>use temperature readings originating from <a href="http://www.ti.com/ww/en/wireless_connectivity/sensortag2015/">TI Sensortags</a> that were</li>
<li>sent over <a href="http://www.bluetooth.com/Pages/low-energy-tech-info.aspx">Bluetooth LE</a> to a <a href="http://beagleboard.org/BLACK">BeagleBone Black</a> acting as a</li>
<li><em><strong>gateway device being managed by <a href="http://www.interdigital.com/solutions/onempower-platform">oneMPOWER</a> in order to,</strong></em></li>
<li>after first being transformed by business logic housed in <a href="http://scriptr.io">scriptr</a>,</li>
<li>control our <a href="http://nest.com">Nest thermostat</a></li>
<li>from a custom <a href="http://bip.io">bip.io</a> workflow, and finally to</li>
<li>set up monitoring and alerting for the whole thing using <a href="http://www.circonus.com">Circonus</a></li>
</ul>
<p>Whew! That's a mouthful! What a relief that wot.io's <a href="/an-architecture-for-internet-of-things-applications">loosely-coupled architecture</a> supports the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY principle</a> so that we only had to modify the third bullet. The rest of that complex data flow just continued to work just like before!</p>
<p>From data in motion to data at rest, and with an <a href="http://www.wot.io/partners/">ever-growing selection of data service partners</a>, wot.io has you covered, including enterprise-ready solutions like oneMPOWER. Ready for more? Head over to <a href="http://connect.wot.io/onempower">wot.io</a> and dig in!</p>
<script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script>