IoT Cloud Services

Introduction

The CloudOut and CloudIn widgets send and receive data from open IoT (Internet of Things) cloud services including Xively (formerly COSM and Pachube), Open.Sen.se, and ThingSpeak. These services allow for devices, software, or people to post and receive feeds from anywhere in the world. For example, a gesture sensor in Pasadena could send its values to the cloud, and video displays in London and Beijing can access the latest value of that sensor, and play different scenes based on the Pasadena sensor value.

The key thing about these services is that feeds can be posted and retrieved from anywhere, without the need to have a direct connection between the sender and receiver. The IoT cloud service handles the asynchronous reception, storage, and delivery of the feeds.

Quick Start

Once you’ve selected a service, gotten an account, and set up your API Key, Channel and DataFeed, you are ready to use the CloudOut and CloudIn widgets. In the parameters of either widget, enter the following:

  • apiKey – A long string of characters that’s like a password, assigned to you by the service. E.g.  “CwOwSto2D6a7w1sQ9kr”
  • channel – The descriptive name of the device, environment, or software that generates a set of DataFeeds. E.g. “arduino”.
  • dataFeed – The descriptive name of a specific set of data from the channel. E.g. “analogIn3”
  • sampleRate – This is how often you will send or receive data from a dataFeed in seconds.

With these parameters set:

  • CloudIn – Give the widget an instance name so other widgets (e.g. ClipControl, AnalogOut) to use values from this cloud data feed
  • CloutOut – Set the inputSource parameter of CloudOut to listen to another widget (e.g. AnalogIn) to send those values to this cloud data feed

IoT Cloud Services

The widgets support three similar services.

  • Xively – The original service (formerly COSM, Pachube), many features but a little complex.
  • Open.Sen.se – Currently in Beta, this new service allows you to combine feeds for more rich outcomes
  • ThingSpeak – The underlying system for IO Bridge, also available as open-source server you can install on your own system

While the terminology and specifics vary somewhat, these services all work in basically the same way. The table below compares the services to each other. First, here are the important concepts:

  • API Key – In order to securely communicate with the cloud service and keep your data private, the services require that you register with them and obtain an API key that’s used in the communications with the service – like a password. Without an API Key, you won’t be able to send or get data feeds. Some services have a single key for all your feeds, while others allow multiple keys with different abilities.
  • Channels – The top level of data organization represents an environment, device, or software, which we’re calling a channel.
  • Data Feeds – Within each channel, there are individual data feeds that represent a specific sensor, software input, or human input. These are often numeric representations, like a value from 0-1023 to indicate the position of a knob. But they may also be textual, and perhaps in the future, images.
  • Data Rate Limit – Since these cloud services support thousands of users all over the world, and their services are accessed through the Internet, they limit how often you can send or receive values for a data feed.
NETLab Widgets  Xively Open.Sen.se (in Beta) ThingSpeak
API Key apiKey API Key Sen.se Key API Key
Channel Name channel Device Feed ID Channel Channel
Data Feed Name dataFeed Channel Name – Datastream Feed Field
Data Feeds per Channel 1 per widget Unlimited Unlimited 8
Data Rate Limit 1 per second 100 times per minute Not specified Once every 15 secs (unlimited if running on your own server)
API Documentation Api Docs APi Docs Api Docs
Service overview Overview Overview Overview
Get an account Account Account Account

Setting up an Account, Channel, Data Feed, and API Key

Before you can use the CloudIn and CloudOut widgets, you need to sign up with the service and set up at least one channel and data feed. In addition, you need to set up or use the default API Key. After you get an account for your chosen service (see links in table above), here’s how to do the rest for each service:

Xively

  1. Create an account here: xively.com/signup/
  2. Log in and from your “Development Devices” page click on the “+ Add Device” button
  3. Fill in the form –
    1. give a simple name to your device/feed – e.g. classArduino
    2. give a description – e.g. at my desk in the MDP studio
    3. select Public Device so anyone can access it
  4. With the device created, add a channel be selecting “+ Add Channel” ,  fill out the fields, and select Save Channel
    classArduino_-_Xively
  5. Write down the following information to put into your Cloud widgets (click on image below to see detail)
    1. Feed ID  – This is the “channel” for your widget
    2. Channel Name – This is the “dataFeed” for your widget
    3. API Key – This is the “apiKey” for your widget

classArduino_-_Xively-13

 

For more things you can do with Xively, check out this page that explains how to trigger a tweet or SMS message with the values of your feeds using the Zapier.com service.

Open.Sen.se

  1. Log in and from your SenseBoard, click on “add more Channels”
  2. Click on “+ Register” for the Arduino
  3. On the next page “Add a Device > Custom Device” click on “Get Started”
  4. Under “Describe your Device, give a Device name
  5. Record the new Device name as your widget channel
  6. Set “Device is” to Other
  7. Give an optional description
  8. Under “How will it interact with Sen.se, use the defaults of HTTP Posting and HTTP Polling
  9. Select Save and continue
  10. Under “Add a feed” put in a name for your feed
  11. Select “Input” for Which way does your feed go
  12. Under “What type of data…” select the first option, “Numbers with decimals (float)”
  13. Under “Choose a unit” it’s your choice, but as a default you can select “No units required”
  14. Optionally put in minimum or maximum values
  15. Select Save Feed
  16. On the next page select Finish
  17. On the next Device Settings page, record the number next to your new feed, which is your dataFeed
  18. API Key – To get your API Key, select the drop-down menu in the upper right corner of the webpage, and select Profile & Settings
  19. Click on “API Key”
  20. Record this text to use in the apiKey parameter of the widget

ThingSpeak – Video Tutorials

  1. Log in, and then select “Channels” from the main navigation
  2. Click on “Create New Device”
  3. Give the new channel a name
  4. Name the first channel, and any others if you want
  5. Select Update Channel
  6. On the next Channel page, record the “Channel ID”, this is your channel
  7. Fields in ThingSpeak are numbered 1-8, and in the widget you use just the number to identify the field as your dataFeed
  8. Also record the “Write API Key” on this page. In ThingSpeak, each Channel has a different API Key that you use in the apiKey widget parameter

Other Services

It should be noted that these services are proliferating as the idea of the Internet of Things becomes more popular. In addition to the open services we’re using, there are more closed systems tied to specific devices. The approach of these systems is to produce a low-cost, small internet enabled device (about the size of a post-it note) with inputs and outputs for sensors and actuators. These include:

 

Code Examples

To help those of you who want to write your own applications outside of the NETLab Toolkit, using the APIs for these services, below are snippets of AS3 code from the CloudIn and CloudOut widgets. While the documentation from these cloud services is pretty good, it took a fair amount of time to research and test the different APIs and use them in a consistent way. These code examples should give you a good baseline no matter what language you will be developing in.

The key thing in using the APIs for all the services is that you must correctly set up the HTTP headers and data for a POST.

  1. Create a header to describe the appropriate type of data being sent which is different for each service
  2. Create a header that includes the API Key
  3. Build the appropriate URL to retrieve or send a value
  4. Retrieving a value: decode the csv, json, or xml response from the server
  5. Sending a value: construct the appropriate csv, json, or xml string, and make that POST data

Retrieving Values – CloudIn

// set up the HTTP requests depending on the service
switch (cloudService) {

     case "cosm":
          headerType = new URLRequestHeader("Content-type", "text/csv");
          headerKey = new URLRequestHeader("X-ApiKey",apiKey);
          urlRequest = new URLRequest("http://api.cosm.com/v2/feeds/" + channel + ".csv?datastreams=" + dataFeed);
          break;

     case "sen.se":
          headerType = new URLRequestHeader("Content-type", "application/json");
          headerKey = new URLRequestHeader("sense_key",apiKey);
          urlRequest = new URLRequest("http://api.sen.se/feeds/" + dataFeed + "/last_event/");
          break;

     case "thingspeak":
          headerType = new URLRequestHeader("Content-type", "application/x-www-form-urlencoded");
          headerKey = new URLRequestHeader("X-THINGSPEAKAPIKEY",apiKey);
          urlRequest = new URLRequest("http://api.thingspeak.com/channels/" + channel + "/field/" + dataFeed + "/last.csv");
          break;
}

urlRequest.method = URLRequestMethod.POST;
urlRequest.data = "";
urlRequest.authenticate = true;
urlRequest.requestHeaders.push(headerType);
urlRequest.requestHeaders.push(headerKey);

// set up the listeners
loader.addEventListener(Event.COMPLETE, completeHandler);
loader.addEventListener(Event.OPEN, openHandler);
loader.addEventListener(ProgressEvent.PROGRESS, progressHandler);
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);

try {
     loader.load(urlRequest);
} catch (error:Error) {
     trace("Unable to load requested document: " + error);
}

function completeHandler(event:Event):void {
     var loader:URLLoader = URLLoader(event.target);
     var theValue:String;

     var valueIndexStart:int;
     var valueIndexEnd:int;
     var searchStr:String;
     var csvArray:Array;       

     switch (cloudService) {
          case "sen.se":
               searchStr = '"value": "';
               valueIndexStart = loader.data.indexOf(searchStr); // find the searchStr in the XML
               valueIndexEnd = loader.data.indexOf('"',valueIndexStart + searchStr.length); // look for the next quote
               theValue = loader.data.substring(valueIndexStart + searchStr.length,valueIndexEnd); // get the value between quotes
               break;

          case "cosm":
               csvArray = loader.data.split('\n'); // get the lines of data into array
               csvArray = csvArray[0].split(','); // split the first line by comma
               theValue = csvArray[2]; // get the third item
               break;

          case "thingspeak":
               csvArray = loader.data.split('\n'); // get the lines of data into array
               csvArray = csvArray[1].split(','); // split the second line by comma
               theValue = csvArray[2]; // get the third item
               break;
     }

     dispatchNetEvent(Number(theValue));
}

Sending Values – CloudOut

// set up the HTTP requests depending on the service

switch (cloudService) {

     case "cosm":
          headerType = new URLRequestHeader("Content-type", "text/csv");
          headerKey = new URLRequestHeader("X-ApiKey",apiKey);
          urlRequest = new URLRequest("http://api.cosm.com/v2/feeds/" + channel + "?_method=put");
          break;

     case "sen.se":
          headerType = new URLRequestHeader("Content-type", "application/json");
          headerKey = new URLRequestHeader("sense_key",apiKey);
          urlRequest = new URLRequest("http://api.sen.se/events/");
          break;

     case "thingspeak":
          headerType = new URLRequestHeader("Content-type", "application/x-www-form-urlencoded");
          headerKey = new URLRequestHeader("X-THINGSPEAKAPIKEY",apiKey);
          urlRequest = new URLRequest("https://api.thingspeak.com/update");
          break;
}

urlRequest.method = URLRequestMethod.POST;
urlRequest.data = "";
urlRequest.requestHeaders.push(headerType);
urlRequest.requestHeaders.push(headerKey);

loader.addEventListener(Event.COMPLETE, completeHandler);
loader.addEventListener(Event.OPEN, openHandler);
loader.addEventListener(ProgressEvent.PROGRESS, progressHandler);
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);

// set up the http request to send a value

switch (cloudService) {

     case "cosm":
          urlRequest.data = dataFeed + "," + inputLast;
          break;

     case "sen.se":
          urlRequest.data = '{ "feed_id" : ' + dataFeed + ', "value" : "' + inputLast + '"}';
          break;

     case "thingspeak":
          urlRequest.data = "field" + dataFeed + "=" + inputLast;
          break;
}

try {
     loader.load(urlRequest);
} catch (error:Error) {
     trace("Unable to load requested document: " + error);
}

Last modified November 18th, 2013