How to create an office or studio status indicator and doorbell with NodeMCU Part #4

Author:

Anders Kitson

Welcome to part 4 of the series, we are going to be connecting the two devices we have built so far through EC2 and an express server using authenticated post requests. We are going to install express, point an event from the button click to our express server. We are going to need to create a https server so we will do that with freenom.

We are going to start where we left off inside our ec2 instance, so you can open that up with

​ssh ubuntu

if you didn't make a ssh config file with the necessary information you can just do

ssh -i key.pem ubuntu@publicdns

Install Node with nvm

So once you've ssh into your instance, you can install nvm so we can then install node. Visit this github repo for nvm info.

Next run the following command

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash

Then run

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

and finally

. ~/.nvm/nvm.sh

nvm should now be ready to install node, just run to install node version 12

nvm install 12

Create a directory and install express

You can check if node installed with node -v

Next we want to create a directory

mkdir server && cd server

Then initialise a npm repo

npm init

Then install express

npm i --save-dev express

Then create a index file

sudo vim index.js

Then add the following code in the index file

const express = require('express')
const app = express()

app.get("/", (req,res) =>{
  res.send("hey")
})

app.listen(3000, () => console.log("server running on port 300"));

Save that file and run and you will see the server is running

node index.js

Change security group on EC2 instance to include port 3000

Then go to loadbalancers -> create -> classic -> 80 to 3000

Assign the security group that is attached to your instance. Change the health check to visit just /

Create your load balancer. I t should take about 5 minutes to come into effect, once it's ready you can visit the DNS name and view our server in the browser.

Once status is 1of1 in service you can view the server in the browser

Now we are going to start connecting the devices together.

First up we want to install pm2, to do so go back to your ec2 instance console and run

npm i pm2 -g

and then run, in order to get our server back online and run in the background

pm2 start index.js​

Next visit aws IoT and go to act -> destinations -> create

So we are going to have to do some configuration here, because we have a http destination but it needs to be https this is where freenom will come in handy.

So first got to https://freenom.com register a free domain and choose the following for your dns

Nameserver: COCO.NS.CLOUDFLARE.COM

Nameserver: CODY.NS.CLOUDFLARE.COM

as seen in the image below

Add cloudflare DNS (you will need a cloudflare account)

Go to cloudflare and add the new site, then choose the free plan.

Then add a cname in the dns Managment. We are using the aws dns address from before

like below

cname record for server

Now we need to a certificate in AWS in order for the domain to be secure so we can send a https request to the server.

So first go to aws -> certificate manager -> request a certificate

Then enter in your domain, choose DNS validation

Then add the cname it gives you in cloudflare seen below

Add this cname to cloudflare dns

Once the dns is saved AWS should eventually verify the dns record and your certificate should be issues, then we are just one step away from using that certificate on our load balancer.

Next visit EC2 -> loadbalancers in the aws console

Go to listeners then edit. Then click add HTTPS then click change SSL, then click choose a certificate, and choose the one you just created from the dropdown seen below

choose certificate

Change the cipher and choose the default and hit save.

Lastly add a https to anywhere in your security groups for the instance seen below

https security group

Your instance is now secure and you should be able to visit your domain with https in front of it. so visit aws IoT and go to act->destinations and paste it in.

First though we want to make a post url to visit, so open up the instance code server/index.js

Modify index.js

Require the following at the top of the file

var https = require("https")
var bodyParser = require("body-parser");

Then add the following post request api route

app.post("/doorbell", (req,res) =>{
  console.log(req.body)
  res.send("hellow world")
});

Next save the file, close it and install body-parser

npm i body-parser

then restart the instance with

pm2 restart index.js

then visit aws and hit create destination to

https://yourdomain.ml/doorbell

Once you are done that open up pm2 logs with

pm2 logs

and then you should see the confirmationToken and go back to aws with that token and copy and paste it under actions -> confirm and enable as seen below

confirmation token

Then you want to create a rule, so visit aws console IoT -> act -> rules give it a name a use this as your query

SELECT * from "devices/#"

Click add action next, and choose send a message to a downstream HTTPS endpoint as seen below

add action to https destination, and choose https://yourdomain.ml/doorbell

Next hit add action, and then create rule. So now anytime the button is pressed it's going to send a request up to the server. Next if you visit your ec2 instance and are in your pm2 logs, when you click the button on the status indicator with two leds, you should get a message in your console.

Next we are going to take the request and send a message to the shadow of the other device and turn it's led on, and then off after three seconds.

Next we are adding the code to turn the led on after button press

Open up server/index.js and add the following code, and make sure you select the board you want to change the led on in the path:

//...
var aws4 = requrie("aws4")

app.use(bodyParser.json())

function request(opts) {
  return new Promise(function(resolve, reject){
    https
      .request(opts, function(res){
        res.pipe(process.stdout)
        res.setEncoding("utf8");
        let body = "";
        res.on("data", (chunk) => (body += chunk));
        res.on("end", () => {
          let json = JSON.parse(body);
          resolve(json.state)
        });
      }).end(opts.body || "")
  });
}

function lightbulb(status){
  var load = JSON.stringify({
    state:{
      desired:{
        on: status,
      },
    },
  })

  request(
    aws4.sign(
      {
        hostname: "a3w047vc4j3fpy-ats.iot.us-east-1.amazonaws.com",
        service: "iotdata",
        region: "us-east-1",
        method: "POST",
        path: "/things/esp8266_112222/shadow",
        headers: {
          "Content-Type": "application/json0",
        },
        body: load,
      },
      {
        secretAccessKey: "yoursecret",
        accessKeyId: "yourid",
      }
    )
  )
}

//...

And then add the following in the post route

app.post("/doorbell", (req,res) =>{
  console.log(req.body)

  lightbulb(true)

  setTimeout(function(){ lightbulb(false)}, 3000);

  res.send("hellow world")
});

save and quit, install aws4 and restart the server with

npm i aws4

pm2 restart index.js

You will notice now when you press the button on the first device the red led will turn on on the second device for three seconds

Now we are going to do the reverse with a answer post request

Go to server/index.js in your instance and write the following new code

function lightbulbAnswer(status){
  var load = JSON.stringify({
    state:{
      desired:{
        on: status,
      },
    },
  })

  request(
    aws4.sign(
      {
        hostname: "a3w047vc4j3fpy-ats.iot.us-east-1.amazonaws.com",
        service: "iotdata",
        region: "us-east-1",
        method: "POST",
        path: "/things/esp8266_7F8471/shadow",
        headers: {
          "Content-Type": "application/json0",
        },
        body: load,
      },
      {
        secretAccessKey: "yoursecret",
        accessKeyId: "yourid",
      }
    )
  )
}


app.post("/answer", (req,res) =>{
  console.log(req.body);
  lightbulbAnswer("free");
  res.send("hello world");
});

Next run through the same create destination in aws IoT destinations as your did for the first one except this time use https://yourdomain.ml/answer and enter it's confirmation token after you have restarted the pm2 server.

Then create a rule for it, but this time make your query different it should be as follow, and create an action to this new destination

SELECT * from "answer1/#"

Then we need to make one last modification in the secondary devices code, open up it's init.js file and change the following line to say answer1 instead of devices, build, flash, connect to wifi and then connect to aws.

// AWS is handled as plain MQTT since it allows arbitrary topics.
  if (AWS.isConnected() || (MQTT.isConnected() && sendMQTT)) {
    let topic = "answer1/" + Cfg.get("device.id") + "/events";
    print("== Publishing to " + topic + ":", message);
    MQTT.pub(topic, message, 0 /* QoS */);
  } else if (sendMQTT) {
    print("== Not connected!");
  }

Once all is said and done you should be able to git the button on the secondary device and change the led to green on the status indicator, showing you are free.

Congrats you made it ;)