A client I was working with wanted communication between systems for a solution to use AWS IoT for broadcasting messages among the computers making up the solution and controlling various computers. I worked on the solution for the system, but had minimal access to their AWS resources, which is consisten with their security policies. Usually, if I have access to the AWS subscription I could use an MQTT viewer that is part of the service for performing some diagnostic tasks. With this client, I didn’t have that access and had to make my own viewer when performing diagnostics.
Making a viewer is pretty easy once you have the resources that you need. I chose WPF because of the speed at which a functional UI could be build along with M2Mqtt as an MQTT client. Before writing any code, there was some work that needed to be done with the certificates. When accessing an AWS MQTT Instance, the information that you will need includes the domain name for the MQTT instance, an Amazon root certificate, a certificate and private key file. You’ll need this information packaged in a pfx
file to easily use it with M2Mqtt.
Packaging these certificates that way is just a matter of running a command. For the files I had, let’s use these names.
000-certificate.pem.crt
– The certificate file for the AWS MQTT Instance000-private.pem.key
– The private key for the AWS MQTT instanceAmazonRootCA1.cer
– The AWS root certificate
Using those file names, the command that I used was as follows.
openssl pkcs12 -export -in .\000-certificate.pem.crt -inkey .\000-private.pem.key -out certificate.pfx -certfile .\AmazonRootCA1.cer
After this command runs, I have a file named certificate.pfx
. I’ll be using this in my .Net viewer.
In the interest of keeping the program reusable, I’ve placed information on the paths to the certificate files in the applications settings. If I needed to change these files post-compilation, they are in a JSON file. These settings include the following.
BrokerDomainName
– The domain name of the MQTT Instance that the application connects toBrokerCertificatePath
– The relative file path to theBrokerRootCertificatePath
– The relative path to the AWS root certificateBrokerPort
– the port that the MQTT Instance is using. Amazon uses port 883ClientPrefix
– Prefix for the name that will be used for identifying this client.DefaultTopic
– The topic that the client will automatically subscribe to upon starting
Concerning the client prefix, every client is identified by a unique string. To ensure the string is unique, I am generating a GUID when the application starts. It’s possible that someone starts more than one instance of the program. For this reason I don’t persist the GUID. A second instance of the program will have a different GUID. But I still want the string to be recognizable as having come from this program. The ClientPrefix
string puts a recognizable string before the GUID to satisfy this need.
The paths in the settings are relative to the application. To load the certificates using these paths and to generate a new client name, I use the following code.
Settings ds = Settings.Default;
var rootCertificatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ds.BrokerRootCertificatePath);
var deviceCertificatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ds.BrokerCertificatePath);
var rootCertificate = X509Certificate.CreateFromCertFile(rootCertificatePath);
var deviceCertificate = X509Certificate.CreateFromCertFile(deviceCertificatePath);
clientName = $"{ds.ClientPrefix}-${Guid.NewGuid().ToString()}";
With that, we have all the information that we need for connecting to the MQTT broker. We can instantiate a MqttClient
, subscribe to it’s events, and start showing the message topics. The call to establish a connection is a blocking call. When it returns, a connection will have been established.
client = new MqttClient(ds.BrokerDomainName, ds.BrokerPort, true, rootCertificate, deviceCertificate, MqttSslProtocols.TLSv1_2);
client.MqttMsgSubscribed += Client_MqttMsgSubscribed;
client.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
try
{
client.Connect(clientName);
var defaultTopic = ds.DefaultTopic;
if (!String.IsNullOrEmpty(defaultTopic))
{
client.Subscribe(new string[] { defaultTopic }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE });
}
} catch(Exception ex)
{
MessageBox.Show(ex.Message, "Could not connect");
}
The primary information of concern in a message is the topic. The message may also have a payload. I capture both of those items. It is possible that the payload contains data that isn’t a string. I’ve decided not to show it, but captured it anyway should I change my mind. To hold the information I use the following class.
public class ReceivedMessage: ViewModelBase
{
private string _topic;
public String Topic {
get { return _topic; }
set
{
SetValueIfChanged(() => Topic, () => _topic, value);
}
}
private byte[] _payload;
public byte[] Payload
{
get { return _payload; }
set
{
SetValueIfChanged(() => Payload, () => _payload, value);
}
}
DateTimeOffset _timestamp = DateTimeOffset.Now;
public DateTimeOffset Timestamp
{
get { return _timestamp; }
set
{
SetValueIfChanged(()=>Timestamp, () => _timestamp, value);
}
}
}
The base class from which this derives, ViewModelBase
, is something I’ve talked about before. See this post if you need more information on how this base class allows this class to be bound to the UI. As new messages come in, I add their content to instances of ReceivedMessage
and add them to an ObservableCollection
. The collection is bound to a ListView
on the UI. The entirety of the main UI window declaration follows. This UI uses only the default code for its code-behind.
<Window x:Class="MessageWatcher.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:vm="clr-namespace:MessageWatcher.ViewModels"
xmlns:local="clr-namespace:MessageWatcher"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.DataContext>
<vm:MainViewModel />
</Grid.DataContext>
<ListView ItemsSource="{Binding MessageList}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Topic}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
With that, I had a working viewer for monitoring the messages as they went by.
Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.
Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet
One thought on “Connecting to AWS IoT’s MQTT Server with .Net”