I recently have been participating in a hackathon in which we're required to build a smart meter solution for the growing population of prepaid meter users in Nigeria. This project is intended to solve some of the everyday problems of users of these prepaid meter devices, for example, a user should be able to turn the meter on and off from the software we're building. This, in fact, was exactly my second task as a backend engineer on the project.
I initially picked this story without having a detailed look at the specs so I felt it was going to be an easy endpoint which I could implement by using a device status
enum on the DB, set it to ON
by default and then create a PATCH
endpoint that takes a meter ID and updates the status to OFF
or ON
depending... Boy was I wrong.
Read your spec guys...
Before I continue rambling tho,
What is an IoT device
It is an acronym which means the Internet of things...
The internet of things, or IoT, is a system of interrelated computing devices, mechanical and digital machines, objects, animals, or people that are provided with unique identifiers (UIDs) and the ability to transfer data over a network without requiring human-to-human or human-to-computer interaction.
Don't pay much attention to the boring long definition tho, were more focused on the Things
part.
A thing, in the context of the Internet of things (IoT), is a representation of a specific device or logical entity. It can be a physical device or sensor (for example, a light bulb or a switch on a wall). It can also be a logical entity like an instance of an application or physical entity that does not connect to AWS IoT but is related to other devices that do (for example, a car that has engine sensors or a control panel).
The solution
The real solution was to connect my device(the meter) to a cloud-based service like AWS or GCP I went with AWS.
What we really want to do here is connect a physical IoT device to a cloud service like AWS and with some code magic build some form of ON
and OFF
switch into the application. Since most of us, won't have access to such hardware to use for full experimenting, The alternative is to create a thing on AWS. This thing
mirrors a real device and if you ever want to go all the way you can get the hardware anytime and sync it to the thing
we would be creating on AWS soon.
Setting up a device(thing) on AWS
- Visit aws.amazon.com on the top right, click the
my account
drop-down and selectAWS management console
- Follow the next few steps to sign in to your account
- After successful login on the top left click on
services
and search for our service of interestIoT core
once found select and you'll be navigated to this page 👇🏽
From the side nav to your left, click secure => policies => create
On the Action input field, start typing *
and select the suggested option. Make sure to fill the form as shown on the screenshot. Scroll to the bottom and click create
.
Still from the side nav to your left click manage => create a single thing
Enter a name for your thing scroll down and click create type
After creating a type, you should see a page like 👇🏽, scroll to the bottom and click next
This last action will navigate you to a new page and you'll see a button to create certificate
click on it and you'll be navigated to this page 👇🏽
Make sure to download all 4 keys, using the download buttons. The last download button should be opened in a new tab. we will need them later on. Click Activate
, scroll down and click Attach a policy
, this action would redirect you to this page 👇🏽
Select the policy we created earlier myIoTPolicy
and click Register Thing
If you can see the screen below 👇🏽 then congratulations you just created a thing
ready to be controlled by code!
Code
Next, we need to implement a switch that can control the device we just created. To do this we need a few things
- An existing Node.js project(setup one or clone this to make your life easier as I would be using it throughout this article.
- AWS-IoT-SDK for node see docs
On your console install the SDK by running
npm i aws-iot-device-sdk
Navigate to server => config
, create a file called awsConfig.js
and paste the following code
import awsIot from 'aws-iot-device-sdk';
const thingShadows = awsIot.thingShadow({
keyPath: '../../Downloads/63efc683ec-private.pem.key',
certPath: '../../Downloads/63efc683ec-certificate.pem.crt',
caPath: '../../Downloads/AmazonRootCA1.pem',
host: 'a1xfh88u91agm5-ats.iot.us-east-2.amazonaws.com',
clientId: 'Meter-001',
region: 'us-east-2',
});
thingShadows.on('status', (thingName, stat, clientToken, stateObject) => {
console.log(JSON.stringify(stateObject.state));
});
export default { thingShadows };
From the code, we simply import aws-iot-device-sdk
, create a thingShadow
, and initialize it with the config keys(be sure to switch up the path to point to your own keys), and then we export that instance.
In case you're wondering how to get your CA_PATH
remember the tab we opened on another window? well if you don't you can visit this link download the contents and save to a file called AmazonRootCA1.pem
. The remaining information can be found on your AWS dashboard.
Next, we want to create a controller function. Navigate to the Controllers folder and create a file called meter.js
, paste the following code
import awsService from '../config/awsSwitch';
/**
* @class MeterController
*/
export default class MeterController {
/**
* @method on
* @description Connects to a device
* @param {*} req
* @param {*} res
* @returns {object} meter
*/
static async on(req, res) {
let clientTokenUpdate;
awsService.thingShadows.register('USER_METER', {}, async () => {
const userMeterState = {
state: {
desired: {
status: 'ON',
},
},
};
clientTokenUpdate = awsService.thingShadows.update(
'USER_METER',
userMeterState
);
if (clientTokenUpdate === null) {
return res.status(400).send({
status: false,
error: 'update shadow failed, operation still in progress',
});
}
return res.status(200).json({
status: true,
message: 'Meter successfully connected',
});
});
}
/**
* @method off
* @description Disconnects a running instance of a device
* @param {*} req
* @param {*} res
* @returns {object} meter
*/
static async off(req, res) {
awsService.thingShadows.end();
return res.status(200).json({
status: true,
message: 'Meter successfully disconnected',
});
}
}
We have two controller functions here ON
and OFF
one registers a thingShadow
and passes in the state ON
and for OFF
we forcefully close the connection.
A few things to note
- A thing shadow is an instance of a physical device like we created on AWS.
- We forcefully close the connection when we call the off endpoint because AWS IoT(to my knowledge) has no form of clean disconnect. what this means, in essence, is that after calling the off endpoint if we need to establish a connection again, we have to restart our server. I have an open issue questioning this design, click here
- Also for simplicity, I won't be including a DB interaction after connecting to a thing, but in a real-world implementation(like what I'm building), you would want to save each
things
details to a DB and link it to a particular user. Your schema for doing that could look something like this
Finally, we have to create our routes for On
and Off
and test our implementation
Navigate to server => routes
and add a new file meter.routes.js
, paste the following code
import express from 'express';
import controllers from '../controllers';
const meterRoute = express.Router();
const {
meterController: { on, off },
} = controllers;
meterRoute.patch('/meter/on', on);
meterRoute.patch('/meter/off', off);
export default meterRoute;
In the index.js
file in the same directory replace the existing code with
import express from 'express';
// auth Routes
import authRoute from './auth.routes';
// meter Routes
import meterRoute from './meter.routes';
// express router
const router = express.Router();
router.use('/auth', authRoute);
router.use(meterRoute);
export default router;
Testing our implementation
Start the server by running npm run start:dev
If you hit an error along these lines
Error: premature close
... You may have missed something in your policy setup(Happened to me the first time) Follow this link to fix it. Or drop a comment 🙂
Finally, using your preferred API testing tool(Insomnia for me). hit the following endpoints
- PATCH
http://localhost:3333/v1/meter/on
- PATCH
http://localhost:3333/v1/meter/off
To verify the connection and disconnection of the device
From your AWS console side nav click on the activity
button, you should see some changes
This updates real-time as well. so you can keep this tab open hit the endpoints and watch the changes happen.
Its a wrap 🎉
That's it guys, let me know what you think, how can we improve on this? Your feedback is important!. I will hopefully see this project through and write more articles on cool things we can do with AWS IoT SDK.
Stay safe and wash your hands!
Top comments (1)
thank you, seems great!