
LavinMQ for IoT: from device to dashboard in minutes
Whether IoT devices go offline, networks wobble, or data arrives in bursts, your consumers shouldn’t even notice. That’s the purpose of a message broker, like LavinMQ, and one of the biggest reasons for using it in IoT. Let this guide demonstrate how.
LavinMQ is a lightweight, fast, and easy-to-use message broker. It stands in the middle to decouple publishers and consumers, buffering messages during outages, managing traffic spikes, and ensuring every message reaches the necessary services. Consumers can subscribe at their own pace, dashboards update in real time, and backends process messages reliably without becoming overloaded.
Why brokers matter for IoT
IoT devices lose power, roam out of coverage, reboot for updates, or sleep to save battery. In short, devices frequently go offline. When they reconnect, traffic often arrives in bursts: thousands of sensors may wake up simultaneously, or alarms may flood the system at once.
Without a broker, these messages either vanish or overwhelm backends. A broker buffers, persists, and routes data so senders and receivers work independently.
That’s why brokers are essential in IoT. LavinMQ brings these capabilities together in a simple, resilient way.
Where LavinMQ fits
Think of LavinMQ as the messaging hub in your IoT system. Devices publish messages to a topic, and consumers subscribe at their own pace. This decoupling makes the system resilient and scalable. With LavinMQ in the middle, both sides work independently and reliably, even when devices go offline.
Here’s how:
- Reliability IoT messages are often tiny but critical. A heart-rate monitor, for example, must not lose readings just because a dashboard went down. LavinMQ stores each message until it’s acknowledged. Even when devices reconnect after downtime, LavinMQ absorbs bursts and delivers data at a steady pace.
- Scalability What happens when you grow from 10 devices to 10,000 or from one consumer to a dozen? Direct connections turn into a fragile tangle. With LavinMQ, devices publish once, and any number of consumers can subscribe. Adding more consumers doesn’t add load to devices.
- Publish once, consume many times A single temperature reading can update a live dashboard, trigger alerts, and store itself in a time-series database simultaneously. Devices don’t duplicate work, LavinMQ handles the fanout.
- Security LavinMQ enforces a clear boundary between the edge (devices) and the core (consumers). Devices connect with TLS, authenticate, and publish only to allowed queues.
Protocols at the edge vs. at the backend
IoT systems range from tiny edge sensors to powerful services in the cloud. A single protocol may not fit everywhere, which is why LavinMQ supports multiple protocols, allowing you to choose the best fit for each part of your stack:
MQTT for devices - Designed for constrained devices and unstable networks. Lightweight, battery-friendly, with QoS levels and retained messages.
Example: a soil-moisture sensor publishes to a topic farm/field3/soil
. LavinMQ receives it, stores it, and makes it available to multiple consumers.
AMQP for backends - Durable queues, acknowledgments, consumer prefetch, and flexible routing.
Example: a temperature reading fans out to a dashboard, a time-series DB, and an anomaly detector—all at once.
MQTT/AMQP over WebSocket for dashboards - Ideal for browsers and quick consumers that need live streaming without extra libraries.
Example: A live production line sends machine status events. The backend processes and stores them, while a dashboard in the browser subscribes via WebSocket to show operators real-time updates without any extra plugins.
Consumer code samples
Here are some sample consumers in different languages:
Python code using the paho-mqtt client library:
import paho.mqtt.client as mqtt
# Broker details
BROKER = "<host-name>"
PORT = 1883
USERNAME = "<vhost:username>"
PASSWORD = "<password>"
# Topics
TOPICT = "lavinmq/home/temperature "
TOPICH = " lavinmq/home/humidity"
def on_connect(client, userdata, flags, reason_code, properties=None):
if reason_code == 0:
print("Connected to broker.")
client.subscribe([(TOPICT, 1), (TOPICH, 1)])
print(f"Subscribed to: {TOPICT}, {TOPICH}")
else:
print(f"Connect failed. reason_code={reason_code}")
def on_message(client, userdata, msg):
print(f"[{msg.topic}] {msg.payload.decode('utf-8', errors='replace')}")
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties=None):
print(f"Disconnected. reason_code={reason_code}, flags={disconnect_flags}")
client = mqtt.Client(
callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
protocol=mqtt.MQTTv311
)
client.username_pw_set(USERNAME, PASSWORD)
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect
client.reconnect_delay_set(min_delay=1, max_delay=30)
client.connect(BROKER, PORT, keepalive=60)
client.loop_forever()
C# code using the .NET MQTTnet client library:
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Protocol;
class Program
{
// Broker details
private const string BROKER = "<broker-name>";
private const int PORT = 1883;
private const string USERNAME = "<vhost:username>";
private const string PASSWORD = "<password>";
// Topics
private const string TOPICT = "lavinmq/home/temperature";
private const string TOPICH = "lavinmq/home/humidity";
static async Task Main()
{
var factory = new MqttFactory();
using var client = factory.CreateMqttClient();
// Handlers
client.UseConnectedHandler(async e =>
{
Console.WriteLine("Connected to broker.");
var subResult = await client.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(TOPICT).WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce))
.WithTopicFilter(f => f.WithTopic(TOPICH).WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce))
.Build());
Console.WriteLine($"Subscribed to: {TOPICT}, {TOPICH}");
});
client.UseDisconnectedHandler(async e =>
{
Console.WriteLine($"Disconnected. Reason: {e.Reason}");
await Task.Delay(TimeSpan.FromSeconds(1));
try
{
await client.ConnectAsync(BuildOptions(), CancellationToken.None);
}
catch (Exception ex)
{
Console.WriteLine($"Reconnect failed: {ex.Message}");
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
client.UseApplicationMessageReceivedHandler(e =>
{
var topic = e.ApplicationMessage.Topic;
var payload = e.ApplicationMessage.Payload == null
? string.Empty
: Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
Console.WriteLine($"[{topic}] {payload}");
});
var options = BuildOptions();
try
{
await client.ConnectAsync(options, CancellationToken.None);
}
catch (Exception ex)
{
Console.WriteLine($"Connect failed: {ex.Message}");
}
await Task.Delay(Timeout.InfiniteTimeSpan);
}
private static IMqttClientOptions BuildOptions()
{
return new MqttClientOptionsBuilder()
.WithClientId($"csharp-subscriber-{Guid.NewGuid():N}")
.WithTcpServer(BROKER, PORT)
.WithCredentials(USERNAME, PASSWORD)
.WithCleanSession()
.WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
.Build();
}
}
JavaScript code using the Node.js MQTT client library
const mqtt = require("mqtt");
// Broker details
const BROKER = "<broker-host>";
const PORT = 1883;
const USERNAME = "<vhost:username>";
const PASSWORD = "<password>";
// Topics
const TOPICT = "lavinmq/home/temperature";
const TOPICH = "lavinmq/home/humidity";
const CLIENT_ID = "terminal-consumer-" + Math.random().toString(16).slice(2);
const client = mqtt.connect({
host: BROKER,
port: PORT,
protocol: PORT === 8883 ? "mqtts" : "mqtt",
username: USERNAME,
password: PASSWORD,
clientId: CLIENT_ID,
clean: true,
keepalive: 60,
reconnectPeriod: 1000,
});
client.on("connect", () => {
console.log("Connected to broker.");
client.subscribe(
[
{ topic: TOPICT, qos: 1 },
{ topic: TOPICH, qos: 1 },
],
(err, granted) => {
if (err) {
console.error("Subscribe error:", err.message);
} else {
console.log(
"Subscribed:",
granted.map((g) => `${g.topic} (QoS ${g.qos})`).join(", "),
);
}
},
);
});
client.on("message", (topic, payload) => {
const text = payload.toString("utf8");
const ts = new Date().toISOString();
console.log(`[${ts}] ${topic}: ${text}`);
});
client.on("reconnect", () => console.log("Reconnecting..."));
client.on("close", () => console.log("Connection closed."));
client.on("error", (err) => console.error("MQTT error:", err.message));
// Graceful exit
process.on("SIGINT", () => {
console.log("\nDisconnecting...");
client.end(true, () => process.exit(0));
});
Ruby code using the thmqtt gem
require 'mqtt'
require 'securerandom'
require 'time'
# Broker details
BROKER = "<broker-host>"
PORT = 1883
USERNAME = "<vhost:username>"
PASSWORD = "<password>"
TOPICS = {
"lavinmq/home/temperature" => 1,
"lavinmq/home/humidity" => 1
}
CLIENT_ID = "terminal-consumer-#{SecureRandom.hex(4)}"
KEEPALIVE_SEC = 60
CLEAN_SESSION = true
USE_TLS = (PORT == 8883)
def connect_and_stream
MQTT::Client.connect(
host: BROKER,
port: PORT,
username: USERNAME,
password: PASSWORD,
client_id: CLIENT_ID,
keep_alive: KEEPALIVE_SEC,
clean_session: CLEAN_SESSION,
ssl: USE_TLS
) do |c|
c.subscribe(TOPICS)
puts "Connected. Subscribed to: #{TOPICS.keys.join(', ')}"
c.get do |topic, message|
ts = Time.now.utc.iso8601
puts "[#{ts}] #{topic}: #{message}"
end
end
end
backoff = 1
loop do
begin
connect_and_stream
backoff = 1
rescue Interrupt
puts "\nDisconnecting..."
break
rescue => e
warn "Disconnected: #{e.class} - #{e.message}"
sleep backoff
backoff = [backoff * 2, 30].min
end
end
Replace the placeholders (<broker-name>, <vhost:username>, <password>)
with the values from your LavinMQ instance’s Overview → MQTT details.
Conclution
Perhaps IoT devices come and go, but your data should remain safe. LavinMQ makes sure every reading gets through, buffering outages, smoothing bursts, and fanning out messages to dashboards, alerts, and storage. It’s lightweight, easy to set up, and ready to scale with your IoT system.
Want to get hands-on? Try our real-time temperature monitoring with LavinMQ and IoT guide.