A Comprehensive Guide to Utilizing the MQTT Protocol

MQTT Overview

MQTT, also known as Message Queuing Telemetry Transport, is a messaging protocol specifically designed for efficient communication between devices in constrained environments. It is particularly useful in scenarios where network bandwidth and device resources are limited. Originally developed by IBM in the late 1990s, MQTT has now become an open standard widely used in various applications, including IoT (Internet of Things)and M2M (machine-to-machine)communication.

Let's take a closer look at MQTT and its common use cases.

  1. Lightweight protocol: MQTT is specifically designed to be lightweight, making it ideal for environments with limited bandwidth or processing power. By utilizing a publish-subscribe messaging model, MQTT reduces overhead compared to traditional client-server communication.
  2. Publish-subscribe model: In MQTT, devices communicate through a publish-subscribe model. Clients, which can be devices or applications, can publish messages on specific topics, while other clients can subscribe to receive messages on those topics. This decoupling of information producers (publishers) and consumers (subscribers) allows for more flexible and scalable communication patterns.
  3. Quality of service (QoS): MQTT provides different levels of quality of service to ensure reliable message delivery. There are three QoS levels available:
  4. Retained messages: MQTT supports retained messages, which means the broker can retain the last message published on a topic. When a new client subscribes to a topic, it immediately receives the most recent retained message for that topic.
  5. Low bandwidth and Power consumption: Due to its lightweight design, MQTT is well-suited for IoT and M2M applications. It minimizes bandwidth usage and power consumption, making it an efficient choice for resource-constrained devices.
  6. Remote monitoring and Control: MQTT is commonly used for remote monitoring and control applications in IoT deployments. For example, it can be used to collect sensor data from distributed sensors, monitor equipment status, and control actuators remotely.
  7. Telemetry and Data Acquisition: MQTT is widely used in applications that involve telemetry and data acquisition, such as environmental monitoring, smart agriculture, asset tracking, and industrial automation. It enables real-time data exchange between devices and backend systems, facilitating data-driven decision-making and analysis.
    Server

The core of MQTT's operation lies in its lightweight publish-subscribe messaging model, which facilitates efficient communication among devices and systems. Here is an overview of how MQTT functions

  1. Publishers and Subscribers: Within the MQTT framework, devices or applications are typically categorized as either publishers or subscribers. Publishers are responsible for transmitting messages, while subscribers are tasked with receiving messages. Devices have the flexibility to function as both publishers and subscribers, enabling versatile communication patterns.
  2. Broker: At the heart of MQTT's architecture is the broker. This server is responsible for receiving messages from publishers, directing them to subscribers, and overseeing communication between clients. Acting as an intermediary, the broker streamlines message exchange between publishers and subscribers.
  3. Topics: Messages in MQTT are structured into topics, which are hierarchical strings used for message categorization. Topics serve as a means to filter and direct messages between publishers and subscribers. Publishers send messages to specific topics, and subscribers receive messages based on their topic preferences.
  4. Connectivity: Clients (publishers and subscribers) establish connections with the MQTT broker using the MQTT protocol. This protocol operates over TCP/IP or other network protocols, ensuring reliable communication across networks. Clients maintain a persistent connection with the broker, enabling efficient message exchange without the need for frequent connection setups.
  5. Message transmission: When a publisher intends to send a message, it publishes the message to a designated topic on the broker. The broker then relays the message to all subscribers subscribed to the relevant topic. Subscribers receive the message and can process it as needed.
  6. Quality of service (QoS): MQTT offers various levels of quality of service to guarantee message delivery reliability. Publishers specify their preferred QoS level when publishing messages. The broker then utilizes the specified QoS level to ensure efficient message delivery.

The broker utilizes the designated QoS level to transmit messages to subscribers. The available QoS levels are as follows

  1. QoS 0: This level ensures best-effort delivery, meaning that the message may be delivered zero or more times.
  2. QoS 1: With this level, the message is delivered at least once, guaranteeing that it will be received by the subscriber.
  3. QoS 2: At this level, the message is delivered exactly once, ensuring that it is received only once by the subscriber.

In addition to QoS levels, MQTT brokers also support retained messages. Retained messages allow brokers to store and retain the last message published on a specific topic. When a new subscriber subscribes to that topic, it immediately receives the most recent retained message. This feature is particularly useful for providing initial state information or configuration to new clients.

To ensure secure communication between clients and the broker, MQTT offers various security mechanisms. These mechanisms include authentication, authorization, and encryption. By implementing these security features, MQTT safeguards sensitive data and prevents unauthorized access to the MQTT infrastructure.

Implementation

We are going to create a WPF project that incorporates the functionalities of both an MQTT client and publisher.

Step 1. Create a new WPF project

Open Visual Studio.

Go to File > New > Project...

Select the WPF App (.NET Core/.NET Framework) template.

Click Create like below

Create like below

Step 2. Install MQTTnet package

Right-click on your project in Solution Explorer.

Select Manage NuGet Packages

Search for MQTTnet.

Click Install to install the package.

Click install

Step 3. Design a User Interface for subscribers

Design a User Interface for subscribers in WPF like below

<Window x:Class="MQTTClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MQTTClient"
        mc:Ignorable="d"
        Title="Subscriber" Height="450" Width="800">
    <Window.Resources>
        <Style x:Key="ButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="#007ACC"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Padding" Value="10"/>
            <Setter Property="Margin" Value="5"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}" CornerRadius="5">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#005F99"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" Value="#004c7f"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Background" Value="#d3d3d3"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <ListView x:Name="MessageListView" Grid.Row="0" Margin="10" Foreground="Red" FontSize="16">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Topic" DisplayMemberBinding="{Binding Topic}" Width="150"/>
                    <GridViewColumn Header="Message" DisplayMemberBinding="{Binding Message}" Width="600"/>
                </GridView>
            </ListView.View>
        </ListView>
        <StackPanel Orientation="Horizontal" Grid.Row="1" Margin="10,0,10,0">
            <Button Content="Connect MQTT" HorizontalAlignment="Stretch" Width="140" Style="{StaticResource ButtonStyle}" Click="Connect_Click"/>
            <Button Content="Disconnect MQTT" HorizontalAlignment="Stretch" Width="140" Style="{StaticResource ButtonStyle}" Click="Disconnect_Click"/>
            <Button Content="Subscribe MQTT" HorizontalAlignment="Stretch" Width="140" Style="{StaticResource ButtonStyle}" Click="Subscribe_Click"/>
            <Button Content="Unsubscribe MQTT" HorizontalAlignment="Stretch" Width="140" Style="{StaticResource ButtonStyle}" Click="Unsubscribe_Click"/>
        </StackPanel>
        <TextBlock Margin="20,0,0,0" x:Name="TxtblckMessage" Grid.Row="2" FontSize="20" FontWeight="Bold" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
    </Grid>
</Window>

Code behind implementation

using MQTTLib.Utility;
using MQTTnet.Client;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
namespace MQTTClient
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        HelperUtility helperUtility;
        private ObservableCollection<MessageModel> _MQTTMessages = new ObservableCollection<MessageModel>();
        public ObservableCollection<MessageModel> MQTTMessages
        {
            get { return _MQTTMessages; }
            set { _MQTTMessages = value; }
        }
        public MainWindow()
        {
            InitializeComponent();
            helperUtility = new HelperUtility();
            helperUtility._mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync; // Handle received messages
            helperUtility.ConnectionStatusEvent += HelperUtility_ConnectionStatusEvent;
        }
        private void HelperUtility_ConnectionStatusEvent(object? sender, EventArgs e)
        {
            TxtblckMessage.Text = "Connected with broker..";
        }
        private Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            return Task.Run(() =>
            {
                if (arg.ApplicationMessage.Payload != null)
                {
                    string payloadAsString = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
                    string topic = arg.ApplicationMessage.Topic;
                    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        MQTTMessages.Add(new MessageModel
                        {
                            Message = payloadAsString,
                            Topic = topic
                        });
                        MessageListView.ItemsSource = MQTTMessages;
                    }));
                }
            });
        }
        private async void Connect_Click(object sender, RoutedEventArgs e)
        {
            await helperUtility.ConnectWithMQTT();
        }
        private async void Disconnect_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await helperUtility._mqttClient.DisconnectAsync();
                TxtblckMessage.Text = "Disconnected with broker..";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private async void Subscribe_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await helperUtility.SubscribeToMQTTTopic();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private async void Unsubscribe_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await helperUtility.UnsubscribeTopic();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

Subscriber view

Subscriber

The utilization of buttons are

  • This button is labeled "Connect MQTT" and is designed to initiate the process of connecting to an MQTT broker when clicked.
  • This button is labeled "Disconnect MQTT" and is intended to trigger the disconnection from the MQTT broker when clicked.
  • Labeled "Subscribe MQTT," this button initiates the process of subscribing to a specific topic on the MQTT broker.
  • This button is labeled "Unsubscribe MQTT" and is meant to trigger the unsubscription from a previously subscribed topic.

Step 4. The design user interface for the publisher

Design User Interface for Publisher in WPF like below

Xaml View

<Window x:Class="MQTTPublisher.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MQTTPublisher"
        mc:Ignorable="d"
        Title="Publisher" Height="450" Width="800">
    <Window.Resources>
        <Style x:Key="ButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="#007ACC"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Padding" Value="10"/>
            <Setter Property="Margin" Value="5"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}"
                                CornerRadius="5">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#005F99"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" Value="#004c7f"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Background" Value="#d3d3d3"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
        </Grid.RowDefinitions>
        <TextBox FontSize="15" Foreground="Green" AcceptsReturn="True" AcceptsTab="True" x:Name="MessageTextBox" Grid.Row="0" Margin="10"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <Button x:Name="PublishButton" Content="Publish MQTT Message" Style="{StaticResource ButtonStyle}" Click="PublishButton_Click" Width="200" Height="40"/>
            <Button x:Name="ConnectBrokerButton" Content="Connect Broker" Style="{StaticResource ButtonStyle}" Click="ConnectBrokerButton_Click" Width="200" Height="40"/>
        </StackPanel>
        <TextBlock Margin="30,0,0,0" x:Name="TxtblckMessage" Grid.Row="2" FontSize="20" FontWeight="Bold" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
    </Grid>
</Window>

Code behind implementation

using MQTTnet.Client;
using MQTTnet;
using MQTTnet.Extensions.ManagedClient;
using System.Windows;
using MQTTnet.Server;
using MQTTLib.Utility;
using MQTTnet.Protocol;

namespace MQTTPublisher
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        HelperUtility helperUtility;
        private IMqttClient _mqttClient;

        public MainWindow()
        {
            InitializeComponent();
            helperUtility = new HelperUtility();
            helperUtility.ConnectionStatusEvent += HelperUtility_ConnectionStatusEvent;
        }

        private void HelperUtility_ConnectionStatusEvent(object? sender, EventArgs e)
        {
            TxtblckMessage.Text = "Connected with broker..";
        }

        private async void PublishButton_Click(object sender, RoutedEventArgs e)
        {
            helperUtility.PushMessageToBroker(MessageTextBox.Text);
        }

        private void ConnectBrokerButton_Click(object sender, RoutedEventArgs e)
        {
            helperUtility.ConnectWithMQTT();
        }
    }
}

Publisher view

Publisher

The utilization of buttons are

  • This button is labeled "Connect Broker" and is designed to initiate the process of connecting to an MQTT broker when clicked.
  • This button is labeled "Publish MQTT Message" and is designed to initiate the process of sending a message to the broker when clicked.

Step 5. Create a service library project

Create a service library project that will contain the following files below

HelperUtility.cs

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System.Windows;
namespace MQTTLib.Utility
{
    public class HelperUtility : BindableBase
    {
        #region Events
        public event EventHandler ConnectionStatusEvent;
        // Method to raise the event
        protected void TriggerConnectionStatusEvent(EventArgs e)
        {
            ConnectionStatusEvent?.Invoke(this, e);
        }
        #endregion
        public IMqttClient _mqttClient;
        public HelperUtility()
        {
            InitializeMQTTClient();
        }
        public async void InitializeMQTTClient()
        {
            _mqttClient = new MqttFactory().CreateMqttClient();
        }
        public async Task ConnectWithMQTT()
        {
            var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
            try
            {
                var clientOptionsBuilder = new MqttClientOptionsBuilder().WithTimeout(TimeSpan.FromSeconds(10))
                   .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500)
                   .WithClientId(MQTTConfiguration.CLIENTID)
                   .WithCleanSession(MQTTConfiguration.BROKERCLEANSESSION)
                   .WithCredentials("", "")
                   .WithRequestProblemInformation(MQTTConfiguration.REQUESTPROBLEMINFORMATION)
                   .WithRequestResponseInformation(MQTTConfiguration.REQUESTRESPONSEINFORMATION)
                   .WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
                clientOptionsBuilder.WithTcpServer(MQTTConfiguration.BROKERADDRESS, MQTTConfiguration.BROKERPORT);
                var resultSet = await _mqttClient.ConnectAsync(clientOptionsBuilder.Build(), timeout.Token);
                if (resultSet != null)
                {
                    if (resultSet.ResultCode == MqttClientConnectResultCode.Success)
                    {
                        TriggerConnectionStatusEvent(null);
                    }
                }
            }
            catch (Exception ex)
            {
                // Handle exception
            }
        }
        public async Task DisconnectBroker()
        {
            try
            {
                // Disconnect from the MQTT broker when the application is closed
                await _mqttClient.DisconnectAsync();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public async Task<bool> SubscribeToMQTTTopic()
        {
            try
            {
                var topicFilter = new MqttTopicFilterBuilder().WithTopic(MQTTConfiguration.MQTT_TOPIC)
                   .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
                   .WithNoLocal(false)
                   .WithRetainHandling(MqttRetainHandling.SendAtSubscribe)
                   .WithRetainAsPublished(true)
                   .Build();
                var subscribeOptionsBuilder = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topicFilter);
                var subscribeOptions = subscribeOptionsBuilder.Build();
                await _mqttClient!.SubscribeAsync(subscribeOptions).ConfigureAwait(false);
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
        public async Task UnsubscribeTopic()
        {
            try
            {
                await _mqttClient.UnsubscribeAsync(MQTTConfiguration.MQTT_TOPIC);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public async void PushMessageToBroker(string messageFromTextbox)
        {
            try
            {
                var message = new MqttApplicationMessageBuilder()
                   .WithTopic(MQTTConfiguration.MQTT_TOPIC)
                   .WithPayload(messageFromTextbox)
                   .WithRetainFlag(true)
                   .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
                   .WithRetainFlag()
                   .Build();
                await _mqttClient.PublishAsync(message);
                await Task.Delay(1000); // Wait for 1 second
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

BindableBase.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MQTTLib.Utility
{
    public class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Equals(storage, value))
            {
                return false;
            }
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    }
}

MessageModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static System.Reflection.Metadata.BlobBuilder;
namespace MQTTLib.Utility
{
    public class MessageModel
    {
        public string Topic { get; set; } = string.Empty;
        public string Message { get; set; } = string.Empty;
    }
}

MQTTConfiguration.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MQTTLib.Utility
{
    public class MQTTConfiguration
    {
        public static readonly string CLIENTID = "OmatrixTechExplorer-" + Guid.NewGuid().ToString();
        public const string MQTT_TOPIC = "OMATRIXTECH/MQTTMESSAGES";
        public const string BROKERADDRESS = "test.mosquitto.org";
        public const int BROKERPORT = 1883;
        public const bool BROKERCLEANSESSION = true;
        public const bool REQUESTPROBLEMINFORMATION = false;
        public const bool REQUESTRESPONSEINFORMATION = false;
    }
}

Step 6. Appearance during program execution

Appearance during program

Steps to launch the application

  • Run both applications concurrently.
  • In the publisher, click on the button labeled "Connect Broker" to establish a connection with the broker.
  • Enter the desired message in the textbox to be sent to the broker. Press the button “Publish MQTT Message”.
  • Now, in the subscriber application, click on the "Connect MQTT" button.
  • Once the connection with the "Broker" is successfully established, press the 'Subscribe MQTT" button.

Repository Path: "https://github.com/OmatrixTech/MQTTExample"