Developing IoT applications has never been easier thanks to the cloud. All the major cloud vendors provide IoT tools; in this tutorial you'll learn how to build a complete IoT application with AWS IoT.
This article is an excerpt from the book, Enterprise Internet of Things Handbook, written by Arvind Ravulavaru.
To get started with AWS IoT, you need an AWS account. If you don't have an AWS account, you can create one here.
Once you have created your account, you can log in and navigate to the AWS IoT Console.
Once you are on the AWS IoT Console page, make sure you have selected a region that is close to your location. I have selected the US East (N. Virginia) region as shown in the following screenshot:
We have added two attributes to identify this group easily, as shown in the previous screenshot.
The current screen should look as shown here:
No issues with that. We will create a policy manually and associate it with this certificate in a moment.
With this, we are done with the setup of a Thing.
In the next section, we are going to use Node.js as a client on Raspberry Pi 3 to send data to the AWS IoT.
Now that we have our Thing set up in AWS IoT, we are going to complete the remaining operation in Raspberry Pi to send data.
You will need the following hardware to set up Raspberry Pi 3 on the DHT11 node:
Connect the DHT11 sensor to Raspberry Pi 3 as shown in the following diagram:
Next, start Raspberry Pi 3 and log in to it. On the desktop, create a new folder named AWS-IoT-Thing. Open a new Terminal and cd into this folder.
If Node.js is not installed, please refer to the following steps:
$ sudo apt update $ sudo apt full-upgrade
$ curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - $ sudo apt install nodejs
$ node -v $ npm -v
Now, we will set up the app and write the required code:
$ npm init -y
$ npm install aws-iot-device-sdk --save
$ npm install rpi-dht-sensor --save
{ "name": "AWS-IoT-Thing", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "aws-iot-device-sdk": "^2.2.0", "rpi-dht-sensor": "^0.1.1" } }
Now that we have the required dependencies installed, let's continue:
var awsIot = require('aws-iot-device-sdk'); var rpiDhtSensor = require('rpi-dht-sensor'); var dht = new rpiDhtSensor.DHT11(2); // `2` => GPIO2 const NODE_ID = 'Pi3-DHT11-Node'; const INIT_DELAY = 15; const TAG = '[' + NODE_ID + '] >>>>>>>>> '; console.log(TAG, 'Connecting...'); var thingShadow = awsIot.thingShadow({ keyPath: './certs/db80b0f635-private.pem.key', certPath: './certs/db80b0f635-certificate.pem.crt', caPath: './certs/RootCA-VeriSign-Class 3-Public-Primary-Certification-Authority-G5.pem', clientId: NODE_ID, host: 'a1afizfoknpwqg.iot.us-east-1.amazonaws.com', port: 8883, region: 'us-east-1', debug: false, // optional to see logs on console }); thingShadow.on('connect', function() { console.log(TAG, 'Connected.'); thingShadow.register(NODE_ID, {}, function() { console.log(TAG, 'Registered.'); console.log(TAG, 'Reading data in ' + INIT_DELAY + ' seconds.'); setTimeout(sendData, INIT_DELAY * 1000); // wait for `INIT_DELAY` seconds before reading the first record }); }); function fetchData() { var readout = dht.read(); var temp = readout.temperature.toFixed(2); var humd = readout.humidity.toFixed(2); return { "temp": temp, "humd": humd }; } function sendData() { var DHT11State = { "state": { "desired": fetchData() } }; console.log(TAG, 'Sending Data..', DHT11State); var clientTokenUpdate = thingShadow.update(NODE_ID, DHT11State); if (clientTokenUpdate === null) { console.log(TAG, 'Shadow update failed, operation still in progress'); } else { console.log(TAG, 'Shadow update success.'); } // keep sending the data every 30 seconds console.log(TAG, 'Reading data again in 30 seconds.'); setTimeout(sendData, 30000); // 30,000 ms => 30 seconds } thingShadow.on('status', function(thingName, stat, clientToken, stateObject) { console.log('received ' + stat + ' on ' + thingName + ':', stateObject); }); thingShadow.on('delta', function(thingName, stateObject) { console.log('received delta on ' + thingName + ':', stateObject); }); thingShadow.on('timeout', function(thingName, clientToken) { console.log('received timeout on ' + thingName + ' with token:', clientToken); });
In the previous code, we are using awsIot.thingShadow() to connect to our AWS Thing that we have created. To the awsIot.thingShadow(), we pass the following options:
We will connect to the preceding host with our certificates. In the thingShadow.on('connect') callback, we call thingShadow.register() to register. We need to register only once per connection. Once the registration is completed, we will start to gather the data from the DHT11 sensor and, using thingShadow.update(), we will update the shadow. In the thingShadow.on('status') callback, we will get to know the status of the update.
Save the file and execute the following command:
$ sudo node index.js
We should see something like this:
As you can see from the previous logs on the console screen, the device first gets connected then registers itself. Once the registration is done, we will wait for 15 seconds to transmit the first record. Then we wait for another 30 seconds and continue the process.
We are also listening for status and delta events to make sure that what we have sent has been successfully updated.
Now, if we head back to the AWS IoT Thing page in the AWS Console and click on the Shadow tab, we should see the last record that we have sent update here:
Underneath that, you can see the metadata of the document, which should look something like:
{ "metadata": { "desired": { "temp": { "timestamp": 1517888793 }, "humd": { "timestamp": 1517888793 } } }, "timestamp": 1517888794, "version": 16 }
The preceding JSON represents the data structure of the Thing's data, assuming that the Thing keeps sending the same structure of data at all times.
Now that the Thing is sending data, let us actually read the data coming from this Thing.
There are two approaches as to how you can get the shadow data:
The following example used the MQTTS approach to fetch the shadow data. Whenever we want to fetch the data of a Thing, we publish an empty packet to the $aws/things/Pi3-DHT11-Node/shadow/get topic. Depending on whether the state was accepted or rejected, we will get a response on $aws/things/Pi3-DHT11-Node/shadow/get/accepted or $aws/things/Pi3-DHT11-Node/shadow/get/rejected, respectively.
For testing the data fetch, you can either use the same Raspberry Pi 3 or another computer. I am going to use my MacBook as a client that is interested in the data sent by the Thing.
In my local machine, I am going to create the following setup, which is very similar to what we have done in Raspberry Pi 3:
$ npm init -y
$ npm install aws-iot-device-sdk --save
var awsIot = require('aws-iot-device-sdk'); const NODE_ID = 'Pi3-DHT11-Node'; const TAG = '[TEST THING] >>>>>>>>> '; console.log(TAG, 'Connecting...'); var device = awsIot.device({ keyPath: './certs/db80b0f635-private.pem.key', certPath: './certs/db80b0f635-certificate.pem.crt', caPath: './certs/RootCA-VeriSign-Class 3-Public-Primary-Certification-Authority-G5.pem', clientId: NODE_ID, host: 'a1afizfoknpwqg.iot.us-east-1.amazonaws.com', port: 8883, region: 'us-east-1', debug: false, // optional to see logs on console }); device.on('connect', function() { console.log(TAG, 'device connected!'); device.subscribe('$aws/things/Pi3-DHT11-Node/shadow/get/accepted'); device.subscribe('$aws/things/Pi3-DHT11-Node/shadow/get/rejected'); // Publish an empty packet to topic `$aws/things/Pi3-DHT11-Node/shadow/get` // to get the latest shadow data on either `accepted` or `rejected` topic device.publish('$aws/things/Pi3-DHT11-Node/shadow/get', ''); }); device.on('message', function(topic, payload) { payload = JSON.parse(payload.toString()); console.log(TAG, 'message from ', topic, JSON.stringify(payload, null, 4)); });
$ node index.js
We should see something similar to what is shown in the following console output:
This way, any client that is interested in the data of this Thing can use this approach to get the latest data.
You can also use an MQTT library in the browser itself to fetch the data from a Thing. But do keep in mind this is not advisable as the certificates are exposed. Instead, you can have a backend microservice that can achieve the same for you and then expose the data via HTTPS.
With this, we conclude the section on posting data to AWS IoT and fetching it. In the next section, we are going to work with rules.
Now that we have seen how a client can read the data of our Thing on demand, we will move on to building a dashboard, where we show data in real time.
For this, we are going to use Elasticsearch and Kibana.
Elasticsearch is a search engine based on Apache Lucene. It provides a distributed, multi-tenant capable, full-text search engine with an HTTP web interface and schema-free JSON documents.
Read more about Elasticsearch at http://whatis.techtarget.com/definition/ElasticSearch.
Kibana is an open source data visualization plugin for Elasticsearch. It provides visualization capabilities on top of the content indexed on an Elasticsearch cluster. Users can create bar, line, and scatter plots, or pie charts and maps on top of large volumes of data.
Read more about Kibana at https://www.elastic.co/products/kibana.
As we have seen in the architecture diagram, we are going to create a rule in AWS IoT. The job of the rule is to listen to an AWS topic and then send the temperature and humidity values from that topic to an Elasticsearch cluster that we are going to create using the AWS Elasticsearch Service (https://aws.amazon.com/elasticsearch-service/).
The cluster we are going to provision on AWS will also have a Kibana setup for easy visualizations.
We are going to use Kibana and build the visualization and then a dashboard from that visualization.
We are going to use Elasticsearch and Kibana for a basic use case.
The reason I have chosen to use Elasticsearch instead of building a simple web application that can display charts is because we can do way more than just building dashboards in Kibana using Elasticsearch. This is where the IoT Analytics comes in.
We are not going to explore IoT analytics per se, but this setup should give you an idea and get you started off.
Before we proceed further, we are going to provision a new Elasticsearch cluster.
Do note that the cluster we are going to provision is under free tier and has a limitation of resources. Read more about the limitations at https://aws.amazon.com/about-aws/whats-new/2017/01/amazon-elasticsearch-service-free-tier-now-available-on-t2-small-elasticsearch-instances/. Neither Packt Publishing nor me is in any way responsible for any billing that happens as a by-product of running any example in this book. Please read the pricing terms carefully before continuing.
To set up Elasticsearch, head over to the Amazon Elasticsearch Service console or use the services menu on the of AWS console page to reach the Amazon Elasticsearch Service console page. You should see a screen similar to what is shown here:
Once the process has started, it will take up to 10 minutes for the domain to be provisioned.
Once the domain is provisioned, we should see a similar screen to what is illustrated here:
Here we have our Endpoint, to which data will be sent for indexing. And we also have the URL for Kibana.
When we click on the Kibana URL, after it loads, you will be presented with the following screen:
The previous screen will change once we start indexing data. In the next section, we are going to create an IAM role.
Now that we have Elasticsearch and Kibana up and running, we will get started with setting up an IAM role. We will be using this role for the IoT rule and to put data into Elasticsearch:
Now that we have Elasticsearch up and running, as well as the IAM role needed, we will create the IoT Rule to index incoming data into Elasticsearch.
To get started, head over to AWS IoT and to the region where we have registered our Thing:
Field | Value |
Name | ES_Indexer |
Description | Index AWS IoT topic data to Elasticsearch service |
SQL version | 2016-03-23 |
Attribute | cast(state.desired.temp as decimal) as temp, cast(state.desired.humd as decimal) as humd, timestamp() as timestamp |
Topic filter | $aws/things/Pi3-DHT11-Node/shadow/update |
Condition |
SELECT cast(state.desired.temp as decimal) as temp, cast(state.desired.humd as decimal) as humd, timestamp() as timestamp FROM '$aws/things/Pi3-DHT11-Node/shadow/update'
Field | Value |
Domain Name | pi3-dht11-dashboard |
Endpoint | Will get auto selected |
ID | ${newuuid()} |
Index | sensor-data |
Type | dht11 |
IAM role name | iot-rules-role |
Before we continue, we need to configure Elasticsearch to create a mapping. The timestamp that we generate in AWS IoT is of the type long. So, we are going to create a mapping field named datetime with the type date.
From a command line with cURL (https://curl.haxx.se/) present, execute the following command:
curl -XPUT 'https://search-pi3-dht11-dashboard-tcvfd4kqznae3or3urx52734wi.us-east-1.es.amazonaws.com/sensor-data?pretty' -H 'Content-Type: application/json' -d' { "mappings" : { "dht11" : { "properties" : { "timestamp" : { "type" : "long", "copy_to": "datetime" }, "datetime" : {"type": "date", "store": true } } } } } '
Replace the URL of Elasticsearch in the previous command as applicable. This will take care of creating a mapping when the data comes in.
Now that the entire setup is done, we will start pumping data into the Elasticsearch:
Now that we have the data coming in, we will create a new visualization and then add that to our dashboard:
This is our temperature and humidity data over a period of time. As you can see, there are plenty of options to choose from regarding how you want to visualize the data:
Now we have our own dashboard, which show the temperature and humidity metrics:
This wraps up the section on building a visualization using IoT Rule, Elasticsearch, and Kibana.
With this, we have seen the basic features and implementation process needed to work with the AWS IoT platform.
If you found this post useful, do check out the book, Enterprise Internet of Things Handbook, to build a robust IoT strategy for your organization.
5 reasons to choose AWS IoT Core for your next IoT project
Should you go with Arduino Uno or Raspberry Pi 3 for your next IoT project?