Thats It! We are pretty much done as far as Amazon IoT and DynamoDB setup. It was quite a rigmarole wasn't it? Lots of steps that have to be done in a certain order. But the good news is that once this is done, the rest of the project is quite easy, AND FUN!
Installing Certificates
Oh, Wait - One more slightly tedious step to do. Remember those 4 certificates we downloaded much earlier? Now is the time we need to put them to good use (well, 3 out of the 4 at least). We need to copy these certificates to the Onion. I found it easiest to copy and paste the text contents of the certificate over onto the '/home/certs' folder on the Onion. I simply used the web interface editor to create the files in the '/home/certs' folder and paste the contents of the certificate I downloaded. The three certificates I needed (and which I copied and renamed) are:
- VeriSign-Class3-Public-Primary-Certification-Authority-G5.pem -> /home/certs/rootCA.pem
- x1234abcd56ef-certificate.pem.crt -> /home/certs/certificate.pem
- x1234abcd56ef-private.pem.key -> /home/certs/private.key
As you can see, I shortened down the file name for ease of handling, and put them all into one folder for easy access from my Node.js app too. That's it. Once done, you don't have to muck about with certificates any more.
Exactly where you store the certificates or what you call them is not important, you just need to know the details later when writing the Node.js script.
Writing Code
Ok, back to the Omega Onion now, where we will write the code to grab information from the HDT22 and transmit it to Amazon IoT. This is where the rubber hits the road. Using nano, or the web editor on the Onion, create a file called '/home/app.js' and enter the following:
var util = require('util');
var spawn = require('child_process').spawn;
var execFile = require('child_process').execFile;
var mosqparam = [
'--cafile', '/home/certs/rootCA.pem',
'--cert', '/home/certs/certificate.pem',
'--key', '/home/certs/private.key',
'-h', 'a1b2c3d4e5f6g7.iot.us-east-1.amazonaws.com',
'-p', '8883'
];
setInterval(function() {
execFile('/root/checkHumidity/bin/checkHumidity', ['6','DHT22'], function(error, stdout, stderr) {
var dataArray = stdout.split("\n");
var logDate = new Date()
var postData = {
datetime: logDate.toISOString(),
temperature: parseFloat(dataArray[1]),
humidity: parseFloat(dataArray[0])
}
// publish to main data queue (for DynamoDB)
execFile('mosquitto_pub', mosqparam.concat('-t', 'temp-humidity/Omega-XXXX', '-m', JSON.stringify(postData)), function(error, stdout, stderr) {
// published
});
// publish to device shadow
var shadowPayload = {
state: {
desired: {
datetime: logDate.toISOString(),
temperature: parseFloat(dataArray[1]),
humidity: parseFloat(dataArray[0])
}
}
}
execFile('mosquitto_pub', mosqparam.concat('-t','$aws/things/Omega-XXXX/shadow/update', '-m', JSON.stringify(shadowPayload)), function(error, stdout, stderr) {
// shadow update done
});
});
}, 1000 * 60 * 5);
NOTE: I have obfuscated the name of the Omega device here, as well as the Amazon IoT host name for my own security. You will need to ensure that the host name and device name correspond to your own setups above.
Lets go through this code section by section. At the top are the 'require' statements for the Node.js modules we need. Luckily no NPM installs needed here, as the modules we want are part of the core Node.js install.
Then we define an array called 'mosqparam'. These are actually the parameters that we need to pass to the mosquitto command line each time - mainly so it know the MQTT host (-h) and port (-p) it will be talking to, and where to find the 3 certificates that we downloaded from Amazon IoT and copied across earlier.
Tip: If your application fails to run, it is almost certain that the certificate files either cannot be found, or else they have been corrupted during download or copying across to the Onion. The mosquitto error messages are cryptic at best, and a certificate error doesn't always present to obviously. Take care with this bit.
After this is the meat of the code. We are basically running a function within a javascript setInterval() function which fires once every five minutes.
What this function does is run an execFile() to execute the checkHumidity app that we downloaded and installed earlier. It then takes the two lines that the app returns and splits them by the carriage return (\n) to form an array with two elements. We then create a postData object which contains the temperature, the humidity, and the log time as an ISO8601 string.
Then we transmit that postData object to Amazon IoT by calling execFile() on the 'mosquitto_pub' command that we also installed earlier as part of the mosquitto package. mosquitto_pub basically stands for 'MQTT Publish', and it will send the message (-m) consisting of the postData object translated to JSON, to the topic (-t) 'temp-humidity/Omega-XXXX'.
That is really all we need to do, however, in the code above, I've done something else. Straight after publishing the data packet to the 'temp-humidity/Omega-XXXX' topic, I did a second publish to the '$aws/things/Omega-XXXX/shadow/update' topic as well, with essentially the same data, but with some extra object wrappers around it in shadowPayload.
Why did I do this? Well, the '$aws/things/Omega-XXXX/shadow/update' topic is actually a special Amazon IoT topic which stores the data packet within the 'shadow' copy of the Omega-XXXX thing in the cloud. That means that later on, I can use another software system from anywhere in the world to interrogate the Omega-XXXX shadow in the cloud to see what the latest data readings are.
If for any reason the Onion goes offline or the home internet goes down, I can interrogate the shadow copy to see what and when the last reading was. I don't need to set this up, but for future plans I have, I thought it would be a good idea.
Enough talk - save the above file, lets run the code
cd /home
node app.js
You won't see anything on the screen, but in the background, every 5 minutes, the Omega Onion will read the data and transmit it to. Hopefully it is working.
If it doesn't work - things to check are the location and validity of the certificate file. Also check that your home or work firewall isn't blocking port 8883 which is the port MQTT uses to communicate with Amazon IoT.
Now ideally we want our Node.js app to run as a service on the Omega Onion. That way, if the device reboots or loses power and comes back online, the app will auto start and keep logging data regardless. Fortunately, this is easy as well.
Using nano, create a script file called /etc/init.d/iotapp and save the following in it:
#!/bin/sh /etc/rc.common
# Auto start iot app script
START=40
start() {
echo start
service_start /usr/bin/node /home/app.js &
}
stop() {
echo stop
service_stop /usr/bin/node /home/app.js
}
restart() {
stop
start
}
Save the file, then make it executable:
chmod +x /etc/init.d/iotapp
Now register it to auto-run:
/etc/init.d/iotapp enable
Done. The service should start at bootup, and you can start/stop it anytime from the command line via:
/etc/init.d/iotapp stop
or
/etc/init.d/iotapp start
If you go back to your DynamoDB dashboard, click on the table you created, you should be able to see the packet data being sent and updated every 5 or so minutes.