Using Node.JS and MQTT to Build a Responsive Alerting System

Node.js is growing in popularity as a choice for server-side applications and in the Internet of Things.
laptop with code open

At Hark, we use many communication protocols to interact with devices and systems. The sheer quantity of these can be daunting. However, by working with one protocol at a time, we can figure out the quirks in a manageable manner. In this post, we will take a look at MQTT and build an example project for a letterbox alerting system. Hopefully, you will develop a better understanding of what MQTT is and why it is useful in this situation.

Why use MQTT?

MQTT is a lightweight protocol, which allows the use of less powerful hardware and networks of limited bandwidth. An MQTT message consists of a text topic and a binary payload, allowing for efficient communication between broker and client.

In this post we will implement a Node.JS application utilising the pub-sub mechanisms of MQTT, to highlight the benefits of this lightweight technology.

JavaScript

JavaScript is event-driven and fits very well with pub-sub systems. Therefore, JavaScript can sit nicely within applications that rely on MQTT messaging. Also, given the wide range of packages available from NPM, Node.JS is a great way to play around and get to grips with integrating with MQTT.

What will we be building?

Those definitions may not sound too exciting. Hopefully the following project will highlight how useful MQTT can be. In the world of IoT, we often require a small computer to monitor and control an object – a good example is smart thermostats. A computer monitors a temperature sensor and reacts to changes by setting the desired temperature of a heating system.

MQTT is great for this as devices can subscribe to the relevant topics for control and are able to publish messages to notify other subscribers of their current state. Even better, it consumes just a small portion of the available bandwidth. Very important if the number of these devices is large.

Imagine we have a requirement to notify a client when mail is received through a letterbox. The agreed-upon solution will have a sensor attached to the letterbox and requires a means of sending a message to the interested client(s) so that they can respond as appropriate.

We can publish events related to the letterbox without being concerned about which clients need to be notified. This allows for new functionality to be added with ease if we want to trigger different processes from these events.

The below diagram shows the simple application we will be building.

Figure 1. Diagram of the basic letterbox alerting solution:

At its simplest, this solution will only need a single event – the letterbox being opened. In the future, we could enhance the solution to handle other events such as the box being full, but we will ignore that for now.

Building the application

MQTT Broker

This is the service which acts as a middle-man between the message publishers and subscribers. Messages are sent to the broker and subscribers get their messages from the broker.

We will be using Mosquitto as the MQTT broker, running in a docker container. But other brokers are available.

Firstly, create a configuration file (mosquitto.conf) and add the following content:

listener 1883

allow_anonymous true
require_certificate false

Note here we are using the broker without certificates and accepting anonymous connections. What this means is we do not require user authentication or TLS connections to the broker. This is not a secure way to send messages and should not be used in production!

We can then launch the MQTT broker and mount the configuration file.

docker run -p 1883:1883 -v $(pwd)/mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto

MQTT npm package

The node package we will use is async-mqtt which allows us to use promises in our code as opposed to using callbacks.

The main functions we will be using are:

  • MQTT.connect(brokerUrl: string) – Takes a URL to an MQTT broker and returns a client for publishing and subscribing to topics.
  • client.publish(topic: string, message: string) – Publishes a message to a topic on the broker.
  • client.subscribe(topic: string) – Subscribe to the given topic.
  • client.end – Closes the client connection.

Making an initial connection

This application will consist of 2 files, LetterBox.js which will publish the event when the letter box is opened and OpenAlertService.js which will subscribe to events.

Both applications will need to connect to the same broker and so will use the connect function to do so. Remember to provide brokerUrl as the correct url for your broker. So both files will start with the following code.

import { connect } from 'async-mqtt';

const brokerUrl = 'Add your broker URL here';

const client = connect(`${brokerUrl}`);

The letter box will firstly respond to a connect event by publishing a ‘true’ to the topic letterBox/connected. This provides a means for telling other services that the letter box is ready to publish and receive messages. The connect event is fired when the application first connects to the MQTT broker.

import { connect } from 'async-mqtt';

const brokerUrl = 'Add your broker URL here';

const client = connect(`${brokerUrl}`);

const handleConnect = async () => {
  await client.publish('letterBox/connected', 'true');
}

client.on('connect', handleConnect);

Responding to our first message

For the open alert service, we will handle the connect event by subscribing to the ‘letterBox/connected’ event. This means that we can be confident that we have definitely subscribed to the important topics each time we connect to the broker.

import { connect } from 'async-mqtt';

const brokerUrl = 'Add your broker URL here';

const client = connect(`${brokerUrl}`);

const handleConnect = async () => {
  await client.subscribe('letterBox/connected');
}

const handleLetterBoxConnect = () => { 
  console.log('Letter box connected');
}

// When connected to the broker, subscribe to the letterBox/connected event.
client.on('connect', handleConnect);

// When messages are received, use the correct handler for each topic.
client.on('message', async (topic, message) => {
   switch (topic) {
     case 'letterBox/connected':
       return handleLetterBoxConnect;
     default:
       console.log(`Unable to handle topic ${topic}`);
       break;
   }
});

The opened event

Now that both sides of the application are connected to the broker, we can start to write what should happen when the letterbox is opened.

The open alert service will subscribe to the letterBox/opened topic. When messages are received on this topic, it will invoke some notification functionality e.g. push an alert to a client application. For the scope of this demonstration, we will just log to the console.

import { connect } from 'async-mqtt';

const brokerUrl = 'Add your broker URL here';

const client = connect(`${brokerUrl}`);

const handleConnect = async () => {
  await client.subscribe('letterBox/connected');
}

const handleLetterBoxConnect = () => {
  console.log('Letter box connected');
}

const handleOpened = async () => {
  console.log('Letter box has been opened');
  // Functionality to notify user
}

client.on('connect', handleConnect);

client.on('message', async (topic, message) => {
  switch (topic) {
    // Letter box is connected to the broker
    case 'letterBox/connected':
      return handleLetterBoxConnect;

    // Letter box has been opened!
    case 'letterBox/opened':
      return await handleOpened();

    default:
      console.log(`Unable to handle topic ${topic}`);
      break;
  }
});

Finally, we need the letter box to be able to publish the message when it is opened. In reality, this would be triggered by a hardware module. But for this example, we will use a simple timeout. After 10 seconds, the letterBox/opened event will be published.

import { connect } from 'async-mqtt';

const brokerUrl = 'Add your broker URL here';

const client = connect(`${brokerUrl}`);

const handleConnect = async () => {
  await client.publish('letterBox/connected', 'true');
}

const openLetterBox = async () => {
  console.log(logMessage('Publishing open message'));
  await client.publish('letterBox/opened', 'true');
}

client.on('connect', handleConnect);

// Simulation of letter box being opened
setTimeout(async () => await openLetterBox(), 10_000);

There we have a simple system for notifying users that their letterbox has been opened. This is by no means a production-ready application, but it demonstrates how JavaScript can be used to publish and respond to MQTT messages.

Next Steps

The code could be improved to close the connection when needed and to specify which application is writing the log messages. This would help with debugging the processes and would mean we are in control of when we’re connected to the broker.

To make this production ready, we would need to improve security by implementing TLS and authentication for the broker. We would also need to add error handling to the message handlers.

This is a very basic example, but demonstrates how MQTT can fit nicely with the event-driven nature of Node.JS

Related Content

Welcome to The Age of The Smart Store

No cash, no cards – no tills? If you took this news to the middle ages, they’d have burned you at the stake. But here in the outrageous present tense, all is normal (well, kind of).

Read More

Would you like to find out more about the Hark Platform?

Subscribe to Our Newsletter

Stay up to date with the latest industry news, platform developments and more.