DEV Community

Cover image for How to control an IoT device using AWS in a Node.js application.
Victory Akaniru
Victory Akaniru

Posted on

How to control an IoT device using AWS in a Node.js application.

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 select AWS 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 interest IoT core once found select and you'll be navigated to this page ๐Ÿ‘‡๐Ÿฝ

Alt Text

From the side nav to your left, click secure => policies => create

Alt Text

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

Alt Text

Enter a name for your thing scroll down and click create type

Alt Text

After creating a type, you should see a page like ๐Ÿ‘‡๐Ÿฝ, scroll to the bottom and click next

Alt Text

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 ๐Ÿ‘‡๐Ÿฝ

Alt Text

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 ๐Ÿ‘‡๐Ÿฝ

Alt Text

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!

Alt Text

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

Alt Text

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

Alt Text

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!

Discussion (1)

Collapse
yonatangross profile image
yonatangross

thank you, seems great!