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
- Create an account here: xively.com/signup/
- Log in and from your “Development Devices” page click on the “+ Add Device” button
- Fill in the form –
- give a simple name to your device/feed – e.g. classArduino
- give a description – e.g. at my desk in the MDP studio
- select Public Device so anyone can access it
- With the device created, add a channel be selecting “+ Add Channel” , fill out the fields, and select Save Channel
- Write down the following information to put into your Cloud widgets (click on image below to see detail)
- Feed ID – This is the “channel” for your widget
- Channel Name – This is the “dataFeed” for your widget
- API Key – This is the “apiKey” for your widget
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
- Log in and from your SenseBoard, click on “add more Channels”
- Click on “+ Register” for the Arduino
- On the next page “Add a Device > Custom Device” click on “Get Started”
- Under “Describe your Device, give a Device name
- Record the new Device name as your widget channel
- Set “Device is” to Other
- Give an optional description
- Under “How will it interact with Sen.se, use the defaults of HTTP Posting and HTTP Polling
- Select Save and continue
- Under “Add a feed” put in a name for your feed
- Select “Input” for Which way does your feed go
- Under “What type of data…” select the first option, “Numbers with decimals (float)”
- Under “Choose a unit” it’s your choice, but as a default you can select “No units required”
- Optionally put in minimum or maximum values
- Select Save Feed
- On the next page select Finish
- On the next Device Settings page, record the number next to your new feed, which is your dataFeed
- API Key – To get your API Key, select the drop-down menu in the upper right corner of the webpage, and select Profile & Settings
- Click on “API Key”
- Record this text to use in the apiKey parameter of the widget
ThingSpeak – Video Tutorials
- Log in, and then select “Channels” from the main navigation
- Click on “Create New Device”
- Give the new channel a name
- Name the first channel, and any others if you want
- Select Update Channel
- On the next Channel page, record the “Channel ID”, this is your channel
- Fields in ThingSpeak are numbered 1-8, and in the widget you use just the number to identify the field as your dataFeed
- 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:
- http://www.iobridge.com/
- http://supermechanical.com/twine/
- http://ninjablocks.com/
- http://www.panstamp.com/
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.
- Create a header to describe the appropriate type of data being sent which is different for each service
- Create a header that includes the API Key
- Build the appropriate URL to retrieve or send a value
- Retrieving a value: decode the csv, json, or xml response from the server
- 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