Skip to main content
All CollectionsDeveloper Guides
Adding real time using MQTT + Websockets
Adding real time using MQTT + Websockets

Building an HTML Canvas widget for real-time data visualization with MQTT and WebSockets.

Santiago Pachon Robayo avatar
Written by Santiago Pachon Robayo
Updated over 4 months ago

MQTT over WebSockets is rapidly establishing itself as a crucial technology for IoT applications, providing a seamless, efficient, and dynamic way to interact with IoT data. This protocol facilitates real-time data communication directly through web browsers, making IoT more accessible to a broader audience.

At Ubidots, we have integrated MQTT via WebSockets into our core infrastructure, allowing immediate data retrieval and storage within any JavaScript variable. Unlike other technologies such as Socket.io, where you might have to wait for new data to arrive or write additional code to fetch the most recent value, MQTT offers an instant snapshot of the latest variable status. Maintaining an open subscription allows users to receive continuous, real-time updates without needing extra retrieval mechanisms.

This guide will demonstrate how to create an HTML canvas widget that displays data from a variable in real-time, utilizing MQTT.js and WebSockets to update a gauge chart from the Apache E-charts library.

Import the following 3rd party libraries:

https://unpkg.com/mqtt/dist/mqtt.min.js
https://fastly.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js

Create the HTML element in which the data will be displayed:

<div id="chart-container"></div>

Add CSS styling to the chart

* {
margin: 0;
padding: 0;
}
#chart-container {
position: relative;
height: 100vh;
overflow: hidden;
}

Add the logic of the widget by using the following JavaScript code:

// Define the host to connect to the Ubidots MQTT broker
const host = 'wss://industrial.api.ubidots.com:8084/mqtt';

// Retrieve the token from the built-in library and use it as username to autheticate to the broker.
const username = ubidots.token;

// Define the device and variable labels
const deviceLabel = '<device_label>'; // Replace <device_label> with your device label
const variableLabel = '<variable_label>'; // Replace <variable_label> with your variable label

// Define the topic to subscribe to the last value of the variable
const topic = `/v1.6/devices/${deviceLabel}/${variableLabel}/lv`;

// Generate a random client ID
const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8);

// Define the connection options
const options = {
keepalive: 60,
clientId: clientId,
protocolId: 'MQTT',
protocolVersion: 4,
clean: true,
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
username: username,
will: {
topic: 'WillMsg',
payload: 'Connection Closed abnormally..!',
qos: 0,
retain: false
},
};

// Create an MQTT client instance
const client = mqtt.connect(host, options);

// Connect to the client
client.on("connect", function () {
console.log("Connected to broker");
client.subscribe(topic, function (err) {
if (!err) {
console.log(`Successfully subscribed to the topic: ${topic}`);
} else {
console.error("Subscription error:", err);
}
});
});

// Handle incoming messages from the broker and update the chart
client.on("message", function (topic, message) {
console.log(`Received message: ${message.toString()} on topic: ${topic}`);
const formattedValue = parseFloat(message.toString()).toFixed(2);
const newValue = parseFloat(formattedValue);
if (!isNaN(newValue)) {
myChart.setOption({
series: [
{
data: [
{
value: newValue,
name: "Variable name",
},
],
},
],
});
}
});

// Handle errors and connection close events
client.on('error', function(err) {
console.error('Connection error:', err);
document.getElementById('status').textContent = 'Connection Error!';
});

client.on('close', function() {
console.log('Connection closed');
document.getElementById('status').textContent = 'Connection Closed';
});

// Initialize the Apache e-chart
let dom = document.getElementById('chart-container');
let myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});

// Set the chart options
let option = {
series: [{
type: 'gauge',
center: ['50%', '50%'],
radius: '75%',
startAngle: 200,
endAngle: -20,
min: 0,
max: 60,
splitNumber: 12,
itemStyle: {
color: '#FFAB91'
},
progress: {
show: true,
width: 30
},
pointer: {
show: false
},
axisLine: {
lineStyle: {
width: 30
}
},
axisTick: {
distance: -45,
splitNumber: 5,
lineStyle: {
width: 2,
color: '#999'
}
},
splitLine: {
distance: -52,
length: 14,
lineStyle: {
width: 3,
color: '#999'
}
},
axisLabel: {
distance: -20,
color: '#999',
fontSize: 20
},
detail: {
valueAnimation: true,
offsetCenter: [0, '40%'],
fontWeight: 'bolder',
formatter: '{value} °C',
color: 'inherit'
},
data: [{
value: 20
}]
}]
};

myChart.setOption(option);

// Resize the chart when the window is resized
window.addEventListener('resize', function() {
myChart.resize();
});

This is the result:

Did this answer your question?