Search code examples
c#asp.netblazormqttmqttnet

MQTTnet - Can't Read Incoming Messages From Subscribed Topic (Blazor MAUI)


I have recently been learning about MQTT technology. As a .NET developer, I am trying to use MQTTnet to integrate MQTT protocol into a new software project. MQTTnet is a .NET C# package which can be found here: GitHub: MQTTnet.

I am using BLE devices called Beacons which publish a message to a broker every second. Using MQTTX, I have been able to easily subscribe to this topic and read this incoming data. Every second, it prints a json message like this:

{
  "msg": "advData",
  "gmac": "94A408B059C0",
  "obj": [
    {
      "type": 4,
      "dmac": "1EABA5B9FCE0",
      "uuid": "2686F39CBADA4658854AA62E7E5E8B8D",
      "majorID": 1,
      "minorID": 0,
      "refpower": -55,
      "rssi": -81,
      "time": "2023-02-24 22:25:29.452"
    },
    {
      "type": 4,
      "dmac": "BC5729005A4F",
      "uuid": "7777772E6B6B6D636E2E636F6D000001",
      "majorID": 4,
      "minorID": 9998,
      "refpower": -59,
      "rssi": -34,
      "time": "2023-02-24 22:25:29.452"
    }
  ]
}

At the moment, I am simply trying to print these incoming messages in my Blazor MAUI project. So far, I have been able to successfully connect to my broker, and successfully subscribe to the appropriate topic (I have verified these connections using console prints). However, I cannot figure out how to read these incoming payloads. The MQTTnet posts I have found here are pretty out of date, since there have been a handful of changes with MQTTnet v4. I have been searching through and following all of the MQTTnet documentation available--so far, here is my class:

using System.Diagnostics;
using MQTTnet;
using MQTTnet.Client;


namespace Beacons.Data;

public static class MQTT_Client_Subscribe
{
    public static async Task SubscribeTopic()
    {
        var mqttFactory = new MqttFactory();

        using (var mqttClient = mqttFactory.CreateMqttClient())
        {
            var mqttClientOptions = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString()).WithTcpServer("mqtt.ip.address", 1883).WithCredentials("user", "password").Build();

            mqttClient.ApplicationMessageReceivedAsync += e =>
            {
                //e.ApplicationMessage.ContentType = "application/json";
                Debug.WriteLine("Received application message:");
                e.DumpToConsole();
                return Task.CompletedTask;
            };

            await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);

            var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
                .WithTopicFilter(
                    f =>
                    {
                        f.WithTopic("beacon/publish");
                    })
                .Build();

            var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);

            Debug.WriteLine("MQTT client subscribed to topic.");
            //response.DumpToConsole();
        }
    }
}

I call this class on a Razor page in my protected override async Task OnInitializedAsync(). I can see that the broker connection and topic subscribe are successful. I believe the mqttClient.ApplicationMessageReceivedAsync code is supposed to be the handler for incoming packets. This method appears to be one of the major changes with MQTTnet v4, and I can't find any examples or further documentation on how to use it. The Debug.WriteLine() and e.DumpToConsole() in this method are never printed to my console.

Any help with how to access & print this constant stream of incoming json data to my subscribed topic would be very helpful! Thanks.


Solution

  • You are disposing of your MQTT client immediately after subscribing. You never wait for any messages to be sent. In contrast to that, you are awaiting the end of the Connect and Subscribe methods, which is why they work.

    To get the actual messages, you need to either store your client in a field (without disposing it, so no using statement), or you need to prevent the method from ending, before a message is received. For testing a simple Console.ReadLine() or a Task.Delay(10000) should work, for production you probably need a slightly more sophisticated way to keep your client from ending the connection.