Featured Image - Node.js MQTT

Posted on

by

in

,

MQTT using Node.js with practical examples

Introduction

In the previous post, we discussed how we can publish and subscribe to an MQTT(MQ Telemetry Transport) broker thru WebSocket by using the library mqtt.js and purely coded everything in the browser by using Javascript. This post will recreate the same project but using the Node.js runtime environment.

If you want to see this project in a video format then please see below. You can also watch this on my YouTube channel.

Prerequisites

You should have installed the latest Node.js version in your workstation. I am using Windows PC in developing this project but the steps outlined here apply to a Mac/Linux environment. I am using Visual Studio Code in developing the example projects here.

Related Content:
Install Node.js on Windows

Also, you should have an MQTT broker that you can connect to and must have configured MQTT over WebSocket. I am using a local Mosquitto broker with WebSocket configured in this post. Please see the content below on how and why we need to do this in this post.

Related Content:
Install Mosquitto MQTT Windows
How to enable Websockets in Mosquitto MQTT broker?

How to install mqtt.js in a node.js environment?

First, verify that you have installed the latest Node.js version by executing the below command.

$ node -v
v18.13.0

Next, create a directory that will serve as the root folder of our project.

mkdir mqtt-node
cd mqtt-node

Next, we initialize our root folder with Node.js configuration

npm init -y

Lastly, install the dependency mqtt using NPM

npm install mqtt --save

Now that we have installed the mqtt.js library in our Node.js project then it is time to create two projects that will show how to use it.

What are we building?

When you google for “node MQTT” then the links that are shown on the first page of the search results will show you how to use mqtt.js in a command line interface (CLI) project. I was looking for a way to apply Node.js in one of my Internet of Things (IoT) projects and I was thinking of showing it in a dashboard web application serve thru your browser.

So in this post of mine, I am going to create two Node.js MQTT-based example projects/application that uses the mqtt.js:

  • Node.js MQTT CLI (Command Line Interface) project
  • Node-Express-MQTT web application that displays MQTT message

This is my own implementation and it might be different from yours but I am just sharing what I know so you can use it or evaluate it if this is feasible in your own project. The complete code for the two projects can be found in my GitHub repository.

Node.js MQTT CLI project

Open our root folder in Visual Studio Code and create a directory called “cli” inside it then create two files called the publisher.js and subscriber.js under it.

Node JS MQTT CLI Project files

How to connect to the MQTT broker using mqtt.js?

The MQTT.js#Client class wraps a client connection to an MQTT broker over an arbitrary transport method (TCP, TLS, WebSocket, etc). This Client class handles lots of things and includes:

  • Regular server pings
  • QoS flow
  • Automatic reconnections
  • Start publishing before being connected

This is created by calling the method mqtt.connect()

Open your subscriber.js and publisher.js and place the following code. Make sure that you change the mqttHost variable below to point to your MQTT broker.

const mqtt = require("mqtt");

var mqttClient;

// Change this to point to your MQTT broker or DNS name
const mqttHost = "192.168.100.22";
const protocol = "mqtt";
const port = "1883";

function connectToBroker() {
  const clientId = "client" + Math.random().toString(36).substring(7);

  // Change this to point to your MQTT broker
  const hostURL = `${protocol}://${mqttHost}:${port}`;

  const options = {
    keepalive: 60,
    clientId: clientId,
    protocolId: "MQTT",
    protocolVersion: 4,
    clean: true,
    reconnectPeriod: 1000,
    connectTimeout: 30 * 1000,
  };

  mqttClient = mqtt.connect(hostURL, options);

  mqttClient.on("error", (err) => {
    console.log("Error: ", err);
    mqttClient.end();
  });

  mqttClient.on("reconnect", () => {
    console.log("Reconnecting...");
  });

  mqttClient.on("connect", () => {
    console.log("Client connected:" + clientId);
  });

  // Received Message
  mqttClient.on("message", (topic, message, packet) => {
    console.log(
      "Received Message: " + message.toString() + "\nOn topic: " + topic
    );
  });
}

connectToBroker();

Take note of how the host URL is constructed by the below code as it also supports ‘mqtt‘, ‘mqtts‘, ‘tcp‘, ‘tls‘, ‘ws‘, ‘wss‘, ‘wxs‘, ‘alis‘. I am using an unsecured MQTT connection to my broker so I am using the following URL pattern mqtt://192.168.100.22:1833/. The IP address here refers to the IP Address of my Mosquitto MQTT broker.

// Change this to point to your MQTT broker or DNS name
const mqttHost = "192.168.100.22";
const protocol = "mqtt";
const port = "1883";

To test the code, open a new terminal in your VSCode and cd into the cli directory then you should see the following message shown in your terminal

cd cli
node subscriber.js
Client connected:clientje0fnr

How to subscribe to topics in mqtt.js?

If you want to receive MQTT messages from your broker then you could call the mqtt.Client#subscribe method from your Client class and subscribe to the topic that you want to listen to

function subscribeToTopic(topic) {
  console.log(`Subscribing to Topic: ${topic}`);

  mqttClient.subscribe(topic, { qos: 0 });
}

connectToBroker();
subscribeToTopic("temperature");

Go back to your terminal and run the subscriber.js file again. You should be seeing the following message displayed at your terminal.

function subscribeToTopic(topic) {
  console.log(`Subscribing to Topic: ${topic}`);

  mqttClient.subscribe(topic, { qos: 0 });
}

connectToBroker();
subscribeToTopic("temperature");

How to publish topics in mqtt.js?

In order to publish MQTT Messages to our broker then we just need to call the mqtt.Client#publish method on our Client object. A sample of how I used this method can be seen in the code below.

function publishMessage(topic, message) {
  console.log(`Sending Topic: ${topic}, Message: ${message}`);
  mqttClient.publish(topic, message, {
    qos: 0,
    retain: false,
  });
}

connectToBroker();

publishMessage("temperature", 32)

Open a new terminal but don’t close the running subscriber.js and execute the below code.

cd cli
node publisher.js

You should see the following message displayed on the terminal.

C:\git\mqtt-node\cli>node publisher.js
Sending Topic: temperature, Message: 32
Client connected:client4vjw

If you switch back to the subscriber.js terminal then you should see the following messages.

C:\git\mqtt-node\cli>node subscriber.js
Subscribing to Topic: temperature
Client connected:clienti8zs44
Received Message: 32
On topic: temperature

That is all for the code on how you can use the mqtt.js javascript library in a CLI MQTT project that uses the Node.js runtime.

Node-Express-MQTT web application

The next project that we will be doing is to re-create the previous project that we have done in the previous post but we will be using Node.js and the Express web framework. If you haven’t thoroughly read the previous post then I highly suggest that you read it as I won’t be explaining so much about how this project works in this post. I would be focussing on the Node.js environment configurations in this post.

Node.js Express MQTT sample Web Application

Setup express project

The first thing to do is to install the express web framework by executing the below command.

npm install express

We then create the entry point of our project by creating the following file named app.js and putting the below code. This will bootstrap a barebones express web application.

const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Open a new terminal inside VSCode and enter the below command to start our express web server

C:\git\mqtt-node>node app.js
Example app listening on port 3000

Open your browser and type http://localhost:3000 then you should be seeing the following displayed in your browser

Express Hello World

Install nodemon

To speed up our development then we will install the nodemon package utility so that we don’t need to manually restart our server for any changes. Execute the below command to install this package.

npm install --save-dev nodemon

Next, we configure our startup script by editing the package.json and adding two scripts below (e.g. “prod“, “dev“)

{
  "name": "node-mqtt",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "prod": "node app.js",
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.20"
  }
}

Cancel the previous command that we did to run our express server if it is still running and then execute the below command.

npm run dev

This should open up the same message above.

Design

Node Express Mosquitto Design

The image above is how I structured my project and how the request from the browser would get handled by the different Node.js components.

Create routes

Create a directory name “routes” and create two files publisher.js and subscriber.js

const express = require("express");
const router = express.Router();

// Publisher Home Route.
router.get("/", function (req, res) {
  res.send("Publisher Page");
});

module.exports = router;
const express = require("express");
const router = express.Router();

// Subscriber Home Route.
router.get("/", function (req, res) {
  res.send("Subscriber Page");
});

module.exports = router;

At this point, your project should now look like this.

Node JS MQTT With Routes

Next, edit your app.js and include the following routes

const express = require("express");
const app = express();
const port = 3000;

//routes
const subscriberRouter = require("./routes/subscriber");
const publisherRouter = require("./routes/publisher");

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.use("/subscriber", subscriberRouter);
app.use("/publisher", publisherRouter);

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Next, type in the following URLs in your browser http://localhost:3000/publisher and http://localhost:3000/subscriber. This should open up the following pages in your browser

Publisher Initial Page
Subscriber Initial Page

Install EJS

We will use the ejs as our templating library to generate our HTML web user interfaces. To install ejs then execute the below command

npm install ejs

Next, we edit our app.js and explicitly set our view engine to ejs, and also add the middleware to parse the JSON body of the request.

const express = require("express");
const app = express();
const port = 3000;

// template view engine
app.set('view engine', 'ejs');

// Set the json request body
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//routes
const subscriberRouter = require("./routes/subscriber");
const publisherRouter = require("./routes/publisher");

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.use("/subscriber", subscriberRouter);
app.use("/publisher", publisherRouter);

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Create the views folder where we will create our views and inside it create the pages folder also.

mkdir views\pages

Create file publisher.ejs and place the following code inside it.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>MQTT Node.js Publisher</title>
    <link rel="stylesheet" href="/assets/publisher/publisher.css" />
  </head>
  <body>
    <h1>MQTT Node.js</h1>
    <h2>Publisher</h2>
    <div class="line"></div>
    <form method="post" action="/publish" id="publishForm">
      <ul class="wrapper">
        <li class="form-row">
          <label for="topic">Topic</label>
          <input type="text" id="topic" />
        </li>
        <li class="form-row">
          <label for="message">Message</label>
          <input type="text" id="message" />
        </li>
        <li class="form-row">
          <button type="submit" class="publish">Publish</button>
        </li>
      </ul>
    </form>
    <script src="/assets/publisher/publisher.js"></script>
  </body>
</html>

Also, create the subscriber.ejs and place the following code.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>MQTT Node.js Subscriber</title>
    <link rel="stylesheet" href="/assets/subscriber/subscriber.css" />
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
  </head>
  <body>
    <h1>MQTT Node.js</h1>
    <h2>Subscriber</h2>
    <div class="line"></div>
    <ul class="wrapper">
      <li class="form-row">
        <label for="topic">Topic</label>
        <input type="text" id="topic" />
      </li>
      <li class="form-row">
        <label for="message">Message</label>
        <textarea id="message" name="message" rows="10" readonly></textarea>
      </li>
      <li class="form-row">
        <label for="status">Status</label>
        <input type="text" id="status" readonly />
      </li>
      <li class="form-row">
        <div class="btn-container">
          <button type="button" id="subscribe">Subscribe</button>
          <button type="button" id="unsubscribe">Unsubscribe</button>
        </div>
      </li>
    </ul>
    <script src="/assets/subscriber/subscriber.js"></script>
  </body>
</html>

At this point, your project files should now look like this.

Node JS MQTT With EJS

Setup static files

To serve static content such as HTML/CSS/Javascript from our Express server then we need to create the following folder.

mkdir public

Inside the public directory, create two folders and named them publisher and subscriber then we create the files publisher.css, publisher.js, subscriber.css, and subscriber.js inside both folders. At this point, your project should now contain the following files.

Project Current File Structure

Next, populate the contents of the following files from my GitHub repository.

Note: Make sure to change the host variable in the subscriber.js to point to your own MQTT broker that has WebSocket configured.

  // Change this to point to your MQTT broker
  const host = "ws://192.168.100.22:9001/mqtt";

Edit your app.js and add the static serving code.

const express = require("express");
const app = express();
const port = 3000;

// Serve Static Files
app.use(express.static("public"));
app.use("/assets", express.static("public"));

// template view engine
app.set('view engine', 'ejs');

// Set the json request body
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//routes
const subscriberRouter = require("./routes/subscriber");
const publisherRouter = require("./routes/publisher");

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.use("/subscriber", subscriberRouter);
app.use("/publisher", publisherRouter);

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Next, we need to edit our router/publisher.js and router/subscriber.js such that instead of sending hardcoded text then we will render our template.

const express = require("express");
const router = express.Router();

// Publisher Home Route.
router.get("/", function (req, res) {
  res.render("pages/publisher");
});

module.exports = router;
const express = require("express");
const router = express.Router();

// Subscriber Home Route.
router.get("/", function (req, res) {
  res.render("pages/subscriber");
});

module.exports = router;

If you open http://localhost:3000/publisher/ then it should display the following in your browser.

Node JS MQTT Publisher

Next, if you open http://localhost:3000/subscriber/ then it should display the following in your browser.

Node JS MQTT Subscriber

Create our MQTT Service layer

Let us add a separate service layer that will talk to our MQTT so create a directory named “service”.

mkdir service

Inside our service directory, Let us create a class that will handle our MQTT connection. We have encapsulated all the MQTT logic inside the class.

const mqtt = require("mqtt");

class MQTTService {
  constructor(host, messageCallback) {
    this.mqttClient = null;
    this.host = host;
    this.messageCallback = messageCallback;
  }

  connect() {
    this.mqttClient = mqtt.connect(this.host);

    // MQTT Callback for 'error' event
    this.mqttClient.on("error", (err) => {
      console.log(err);
      this.mqttClient.end();
    });

    // MQTT Callback for 'connect' event
    this.mqttClient.on("connect", () => {
      console.log(`MQTT client connected`);
    });

    // Call the message callback function when message arrived
    this.mqttClient.on("message", function (topic, message) {
      console.log(message.toString());
      if (this.messageCallback) this.messageCallback(topic, message);
    });

    this.mqttClient.on("close", () => {
      console.log(`MQTT client disconnected`);
    });
  }

  // Publish MQTT Message
  publish(topic, message, options) {
    this.mqttClient.publish(topic, message);
  }

  // Subscribe to MQTT Message
  subscribe(topic, options) {
    this.mqttClient.subscribe(topic, options);
  }
}

module.exports = MQTTService;

Create our controller layer

We will separate the business logic of rendering our pages by creating a controller layer.

mkdir controllers

Inside it, we have created publisher.js and subscriber.js controllers.

const mqttService = require("../service/mqttService");

// Change this to point to your MQTT broker
const MQTT_HOST_NAME = "mqtt://127.0.0.1:1883";

var mqttClient = new mqttService(MQTT_HOST_NAME);
mqttClient.connect();

exports.getPublisherPage = async function (req, res) {
  try {
    res.render("pages/publisher");
  } catch (error) {
    return res.status(400).json({ status: 400, message: error.message });
  }
};

exports.publishMQTTMessage = async function (req, res) {
  try {
    const topic = req.body.topic;
    const message = req.body.message;

    console.log(`Request Topic :: ${topic}`);
    console.log(`Request Message :: ${message}`);

    mqttClient.publish(topic, message, {});
    res
      .status(200)
      .json({ status: "200", message: "Sucessfully published MQTT Message" });
  } catch (error) {
    return res.status(400).json({ status: 400, message: error.message });
  }
};
exports.getSubscriberPage = async function (req, res) {
  try {
    res.render("pages/subscriber");
  } catch (error) {
    console.log(error);
    res
      .status(400)
      .json({ status: 400, message: "Error getting subscriber page" });
  }
};

We now can edit our routes to use our controller layer.

const express = require("express");
const router = express.Router();

var publisherController = require("../controllers/publisher");

// Publisher Home Route.
router.get("/", publisherController.getPublisherPage);

router.post("/", publisherController.publishMQTTMessage);

module.exports = router;
const express = require("express");
const router = express.Router();

var subscriberController = require("../controllers/subscriber");

// Subscriber Home Route.
router.get("/", subscriberController.getSubscriberPage);

module.exports = router;

At this point, your project should now look like the below image.

Node JS MQTT Complete

That is all for the code. For a demo of the said project then please see the following video below on how this project works.

Wrap Up

We have successfully created some example projects that utilize MQTT in a Node.js environment in this post.

I hope you learned something. Happy Exploring!

If you like my post then please consider sharing this. Thanks!

3 responses to “MQTT using Node.js with practical examples”

  1. Arduino MQTT Example Project – BMP/BME 280 Weather Station

    […] Related Content: MQTT using Node.js with practical examples […]

  2. Jose Ismael Castro Ruiz Avatar
    Jose Ismael Castro Ruiz

    Excelente contenido, no lo puesto en marcha, pero seguro que esta semana lo hago con el favor de Dios, muchas gracias por compartir, saludos desde Los Mochis Sinaloa México.

    1. donsky Avatar
      donsky

      Gracias! 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *