Using MqTT on Apache Cordova

A great combination to try!!

Update

  1. v0.2.9 is released. New features added.

MqTT (Message Queuing Telemetry Transport) protocol was launched with wide support via multiple languages like Python, C, Java, Javascript, PHP etc. Now considering Javascript, it was observed that HTML5 Websockets had to supported by the running environment. So it could’ve been an obvious choice for Apache Cordova. But the webview component which is the underlying base of Apache Cordova framework, provides support to Websockets if the webview is updated enough to support WebSockets. Plus it runs on JavascriptCore which again creates an overhead on the webview component. By using a native plugin, we can utilise the native features of the platform and access the MQTT specific functions via Javascript, without utilizing much of JavascriptCore.

At Arcoiris Labs we have developed a simple MQTT plugin for Apache Cordova for Android and Windows Phone (still under development). Here is the link.

https://github.com/arcoirislabs/cordova-plugin-mqtt

The instructions to install are covered in ReadMe file. However we will be following a tutorial to actually spin off a working MqTT based app in Apache Cordova supporting any app above Android 4.0 & Windows Phone OS 8.1.

  1. Node.js
  2. GitHub
  3. Apache Cordova CLI toolchain
  4. Android developer environment/Visual Studio 2013.

If you do not have Cordova installed on your machine, you can install it by this command, assuming you have Node.js installed. This command works on all the operating systems

npm install -g cordova

Once you install Cordova, create a project by any name you wish. We will place it in a folder named mqtt with the reverse ID of the app as com.app.mqtt with the app’s name as “MqTT Test” by following command. Now add a platform in the project. As our plugin supports Android and Windows Phone, we are going to add Android platform for example.

cordova create mqtt com.app.mqtt "MQTT Test" && cd mqtt && cordova platform add android

If you wish to add more platforms, you can add “ios” or “wp8” instead of android in the CLI. Now we are going to install the plugin in the project. To add the plugin, use following command. Remember, you need to have git binary in your path. So make sure you have GitHub app installed on your Windows machine, and add the git.exe in the global path. For Linux, you can install git-core package from the repository. Once you have the binary in your path, you can proceed with this step.

cordova plugin add cordova-plugin-mqtt

Now assuming no errors were observed. Build the project to fetch the plugin’s dependencies.

cordova build

Remember, you need to be connected to internet while building the project only for the first time to download the nifty Android library of MQTT. Now once the project is build, open the index.js located in www/js/index.js. Update your index.js to this. To add more granular configuration, please refer to documentation in README on our Github Repository

Update :- We have added a sample app code in our repository. Do check it out.

index.js

/*index.js
*/
var connect = false;
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
document.getElementById("connect").addEventListener('touchend',function(ev){
cordova.plugins.CordovaMqTTPlugin.connect({
url:document.getElementById("url").value, //a public broker used for testing purposes only. Try using a self hosted broker for production.
port:document.getElementById("port").value,
clientId:document.getElementById("clientId").value,
success:function(s){
connect = true;
console.log(JSON.stringify(s));
document.getElementById("connect").style.display = "none";
document.getElementById("disconnect").style.display = "block";
document.getElementById("activity").innerHTML += "--> Success: you are connected to, "+document.getElementById("url").value+":"+document.getElementById("port").value+"<br>"
},
error:function(e){
connect = false;
document.getElementById("activity").innerHTML += "--> Error: something is wrong,\n "+JSON.stringify(e)+"<br>";
document.getElementById("connect").style.display = "block";
document.getElementById("disconnect").style.display = "none";
alert("err!! something is wrong. check the console")
console.log(e);
},
onConnectionLost:function (){
connect = false;
document.getElementById("activity").innerHTML += "--> You got disconnected";
document.getElementById("connect").style.display = "block";
document.getElementById("disconnect").style.display = "none";
}
})
});
document.getElementById("disconnect").addEventListener('touchend',function(e){
document.getElementById("connect").style.display = "block";
document.getElementById("disconnect").style.display = "none";
cordova.plugins.CordovaMqTTPlugin.disconnect({
success:function(s){
connect = false;
document.getElementById("connect").style.display = "block";
document.getElementById("disconnect").style.display = "none";
document.getElementById("activity").innerHTML += "--> Success: you are now disconnected"+"<br>"
},
error:function(e){
document.getElementById("activity").innerHTML += "--> Error: something is wrong, "+e+"<br>";
document.getElementById("connect").style.display = "none";
document.getElementById("disconnect").style.display = "block";
//alert("err!! something is wrong. check the console")
console.log(e);
}
});
});
document.getElementById("subscribe").addEventListener('touchend',function(ev){
if (!connect) {
alert("First establish connection then try to subscribe");
} else {
cordova.plugins.CordovaMqTTPlugin.subscribe({
topic:document.getElementById("topic_sub").value,
qos:0,
success:function(s){
document.getElementById("subscribe").style.display = "none";
document.getElementById("unsubscribe").style.display = "block";
document.getElementById("activity").innerHTML += "--> Success: you are subscribed to the topic, "+document.getElementById("topic_sub").value+"<br>"
//get your payload here
//Deprecated method
document.addEventListener(document.getElementById("topic_sub").value,function (e) {
e.preventDefault();
document.getElementById("activity").innerHTML += "--> Payload for"+e.topic+" topic: "+JSON.stringify(e.payload)+"<br>"
});
cordova.plugins.CordovaMqTTPlugin.listen(document.getElementById("topic_sub").value,function (payload,params,topic,topic_pattern) {
//params will be an empty object if topic pattern is NOT used.
document.getElementById("activity").innerHTML += "--> Payload for"+topic+" topic: "+JSON.stringify(payload)+"<br>"
})
},
error:function(e){
document.getElementById("activity").innerHTML += "--> Error: something is wrong when subscribing to this topic, "+e+"<br>";
document.getElementById("subscribe").style.display = "block";
document.getElementById("unsubscribe").style.display = "none";
//alert("err!! something is wrong. check the console")
console.log(e);
}
});
}
});
document.getElementById("unsubscribe").addEventListener('touchend',function(ev){
cordova.plugins.CordovaMqTTPlugin.unsubscribe({
topic:document.getElementById("topic_sub").value,
success:function(s){
document.removeEventListener(document.getElementById("topic_sub").value);
document.getElementById("unsubscribe").style.display = "none";
document.getElementById("subscribe").style.display = "block";
document.getElementById("activity").innerHTML += "--> Success: you are unsubscribed to the topic, "+document.getElementById("topic_sub").value+"<br>"
document.getElementById("topic_sub").value = "";
},
error:function(e){
document.getElementById("activity").innerHTML += "--> Error: something is wrong, "+e+"<br>";
document.getElementById("subscribe").style.display = "block";
document.getElementById("unsubscribe").style.display = "none";
//alert("err!! something is wrong. check the console")
console.log(e);
}
});
});
document.getElementById("publish").addEventListener('touchend',function(ev){
if (!connect) {
alert("First establish connection then try to publish")
} else {
cordova.plugins.CordovaMqTTPlugin.publish({
topic:document.getElementById("topic_pub").value,
payload:document.getElementById("payload").value,
qos:0,
retain:false,
success:function(s){
document.getElementById("activity").innerHTML += "--> Success: you have published to the topic, "+document.getElementById("topic_sub").value+"<br>";
},
error:function(e){
document.getElementById("activity").innerHTML += "--> Error: something is wrong, "+e+"<br>";
//alert("err!! something is wrong. check the console")
console.log(e);
}
});
}
});
console.log('Received Event: ' + id);
},
append:function (id,s) {
// it is just a string append function. Nothing to do with the MQTT functions
var node = document.createElement("p"); // Create a <li> node
var textnode = document.createTextNode(s); // Create a text node
node.appendChild(textnode); // Append the text to <li>
document.getElementById(id).appendChild(node); // Append <li> to <ul> with id="myList"
}
};
app.initialize();

index.html

<!DOCTYPE html>
<!--

-->
<html>
<head>

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<title>Mqtt Test</title>
</head>
<body>
<div>
<h4>Connect</h4>
<p>P.S:- "mqtt://" protocol is not supported by this plugin. Instead use "tcp://" which works with any broker.</p>
<label for="url">Url:&nbsp;</label><input type="text" id="url" name="url"><br>
<label for="port">Port:&nbsp;</label><input type="number" id="port" name="port"><br>
<label for="clientid">Client id</label><input type="text" id="clientId" name="clientid"><br>
<button id="connect">Connect</button><button id="disconnect" style="display:none">Disconnect</button>
</div>
<div>
<h4>Subscribe</h4>
<label for="topic_sub">Topic:&nbsp;</label><input type="text" id="topic_sub" name="topic_sub"><br>
<button id="subscribe">Subscribe</button><button id="unsubscribe" style="display:none">Unsubscribe</button>
</div>
<div>
<h4>Publish</h4>
<label for="topic_pub">Topic:&nbsp;</label><input type="text" id="topic_pub" name="topic_pub"><br>
<label for="payload">Payload:&nbsp;</label><input type="text" id="payload" name="payload">
<button id="publish">Publish</button>
</div>
<div>
<h4>Activity Log</h4>
<div id="activity"></div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

To view the index.html code, click here

Once you are done, you have actually created a nifty MQTT test app that can give a basic tool to check your MQTT communication. In order to run the app simply type this command to run this app on your device/emulator.

cordova run

Voila!! The app can now enter the world of MQTT officially. We have developed the plugin , as per the Apache Cordova’s Plugin Guidelines, using safe threads that will give you a unhindered experience without blocking the WebCore thread. Hence no lag will be experienced when using this plugin. Plus we have also implemented the Javascript’s Event based callbacks if you do not wish to be confined to the scope of plugin’s method’s callbacks.

You can also maintain the connection to the server by using the Cordova’s official Network Information Plugin by listening to their “offline” and “online” events.

Please comment us down below if you experience any issue or have any suggestion for a feature or workaround. Your inputs will be extremely appreciated. If you wish to fork this plugin repository, feel free to fork it and please try to contribute to this repository. We are actually looking for an iOS developer to develop the iOS version of this plugin.

Cheers!!

Specialized in developing Intelligent Smartphone Applications

Specialized in developing Intelligent Smartphone Applications