0
votes

I'm trying to make Azure IOT Hub behave (more or less) like an MQTT broker.

I want to publish a message on devices/{deviceid}/messages/events/ and then receive it on devices/{deviceid}/messages/devicebound/#.

The library I'm using is M2MQTT and so far, I can connect to the IOT hub and publish a message to the events topic. Using DeviceExplorer I can see the message arriving on the hub.

For some reason the message is not relayed to the devicebound topic and the MqttClient_MqttMsgPublishReceived is never triggered. The event does get triggered when using DeviceExplorer and sending a message from there.

So... I think I'm missing something either in code or in the configuration of the IOT Hub.

Does anybody know what I'm doing wrong?

This is my code:

using Microsoft.Azure.Devices.Common.Security;
using Newtonsoft.Json;
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows.Forms;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;

namespace DemoM2MqttAzure
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private const string IOTHubURI = "TESTIOTHUB.azure-devices.net";
        private const string IOTDeviceID = "IOTDevice";

        private string IOTDeviceUsername = string.Format("{0}/{1}/?api-version=2018-06-30", IOTHubURI, IOTDeviceID);

        private const string IOTDevicePrimaryKey = "xxx";

        private string IOTSubscribeDeviceTopic = string.Format("devices/{0}/messages/devicebound/#", IOTDeviceID);
        private string IOTPublishDeviceTopic = string.Format("devices/{0}/messages/events/", IOTDeviceID);

        //M2MQTT client.
        MqttClient mqttClient;

        private void buttonStartMQTT_Click(object sender, EventArgs e)
        {
            mqttClient = new MqttClient(IOTHubURI, 8883, true, MqttSslProtocols.TLSv1_0, UserCertificateSelectionCallback, null);
            mqttClient.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;

            mqttClient.MqttMsgPublishReceived += MqttClient_MqttMsgPublishReceived;
            mqttClient.MqttMsgPublished += MqttClient_MqttMsgPublished;
            mqttClient.MqttMsgSubscribed += MqttClient_MqttMsgSubscribed;

            string IOTDevicePassword = CreateSharedAccessSignature();
            mqttClient.Connect(IOTDeviceID, IOTDeviceUsername, IOTDevicePassword);

            if(mqttClient.IsConnected)
            {
                MessageBox.Show("Connected!");
            }
            else
            {
                MessageBox.Show("Connection failed.");
            }
        }

        private void MqttClient_MqttMsgSubscribed(object sender, MqttMsgSubscribedEventArgs e)
        {
            byte[] qosLevels = e.GrantedQoSLevels;
            ushort msgId = e.MessageId;
            MessageBox.Show("Event subscribed");
        }

        private string CreateSharedAccessSignature()
        {
            string target = string.Format("{0}/devices/{1}", IOTHubURI, IOTDeviceID);

            return new SharedAccessSignatureBuilder
            {
                Key = IOTDevicePrimaryKey,
                Target = target,
                KeyName = null,
                TimeToLive = TimeSpan.FromDays(360)
            }.ToSignature();
        }

        private void buttonSubscribe_Click(object sender, EventArgs e)
        {
            if(mqttClient.IsConnected)
            {
                mqttClient.Subscribe(new string[] { IOTSubscribeDeviceTopic }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE });
            }
            else
            {
                MessageBox.Show("Not connected.");
            }
        }

        private void buttonPublish_Click(object sender, EventArgs e)
        {
            if (mqttClient.IsConnected)
            {
                var telemetryDataPoint = new
                {
                    deviceId = IOTDeviceID,
                    value = 123.456,
                    text = "abc"
                };

                var json = JsonConvert.SerializeObject(telemetryDataPoint);

                ushort result = mqttClient.Publish(IOTPublishDeviceTopic, Encoding.UTF8.GetBytes(json), MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE, true);
            }
            else
            {
                MessageBox.Show("Not connected.");
            }
        }

        private void MqttClient_MqttMsgPublished(object sender, MqttMsgPublishedEventArgs e)
        {
            if (e.IsPublished)
            {
                MessageBox.Show("Event message published.");
            }
            else
            {
                MessageBox.Show("Message was not published...");
            }
        }

        private void MqttClient_MqttMsgPublishReceived(object sender, uPLibrary.Networking.M2Mqtt.Messages.MqttMsgPublishEventArgs e)
        {
            MessageBox.Show("Event message publish received.");
        }

        private bool UserCertificateSelectionCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(mqttClient.IsConnected)
            {
                mqttClient.Disconnect();
            }
        }
    }
}
2
I'm not sure I understand what you are trying I want to publish a message on devices/{deviceid}/messages/events/ and then receive it on devices/{deviceid}/messages/devicebound/#. Or, you don't quite understand yet how IoT Hub works ;) First clue: IoT Hub is not a full MQTT broker so you should not try to make it behave like one.silent
I guess I don't understand it fully yet 🙂. I know it's not an MQQT broker but how can you trigger the cloud to device communication? I want to publish a message from a device to the cloud and receive that message on another device.Tom Demets
got it. see my answersilent

2 Answers

0
votes

So what you really want to do is this:

Device A --> sends message --> IoT Hub receives it --> sends the message on to Device B --> Device B receives it

You can build this fairly easily, however, not only with IoT Hub. I would propose using a simple Azure Function in the middle:

  • Device A sends messages to IoT Hub.
  • The Azure Function receives all D2C (device-2-cloud) message from the IoT Hub
  • The Function uses the Service SDK of the IoT Hub to send C2D (cloud-2-device) messages to Device B through the IoT Hub
  • Device B listens for messages on ../devicebound and will receive the message there.
0
votes

I think MqttClient.MqttMsgPublishReceived will probably be triggerred when one client receive the message you published. Have MqttClient_MqttMsgSubscribed ever been triggered? Would you like to have a try?
enter image description here
Refer to: MQTT Publish, Subscribe & Unsubscribe - MQTT Essentials: Part 4