[QUERY] How to properly acknowledge IoT hub messages
the-programmer opened this issue · 1 comments
Library name and version
Microsoft.Azure.Devices 1.39.1
Query/Question
Project overview
In my current project I need to control embedded devices (running the Azure SDK for C V1.1.6 on an ESP32, using MQTT) via Azure IoT hub.
So far I managed to get the Device to Cloud (D2C) and Cloud to Device (C2D) functionality working without issues.
The current communication flow for D2C messages is as follows and works great.
- The device sends a message to the IoT hub via MQTT.
- IoT hub writes directly to a CosmosDB container.
- An Azure function has a CosmosDBTrigger on this container.
- The function decodes the body and stores there result in a different container. (This is done to base64 decode the Body of the message)
The current communication flow for C2D messages is as follows and is giving some issues.
- The front-end writes to a CosmosDB container
- An Azure function has a CosmosDBTrigger on this container.
- The function prepares the message and transmits it to IoT hub.
- IoT hub sends the message to the device via MQTT.
Finally, there is a timed Azure function to parse all the feedback from the IoT hub. The feedback is than used to update the container that contained the original command.
This is a timed function since Azure functions doe not allow the "normal" while(true)
method due to a maximum runtime of 5 minutes. This method is inspired by this article.
Some additional details
Program.cs
In the program.cs
of the Azure functions I registered the IoT hub client as follows
//Get the connectionstring for the iotHubServiceClient and create an instance
var iotHubConnectionString = Environment.GetEnvironmentVariable("IotHubConnectionString", EnvironmentVariableTarget.Process);
var iotHubServiceClient = ServiceClient.CreateFromConnectionString(iotHubConnectionString);
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services =>
{
//Other code
//Add the IotHub Serviceclient
services.AddSingleton(iotHubServiceClient);
})
.Build();
When it started
Before setting the Ack
to DeliveryAcknowledgement.Full
I didn't notice this issue (and as far as I can tell it wasn't there).
Timed function code
The timed function had got the following code
//Get the feedbackReceiver
var feedbackReceiver = _iotHubServiceClient.GetFeedbackReceiver();
//Create a cancellationToken with a timeout of 1 minute
var timeoutCancellationTokenSource = new CancellationTokenSource(new TimeSpan(0, 1, 0));
//Start waiting for new messages
var message = await feedbackReceiver.ReceiveAsync(timeoutCancellationTokenSource.Token);
_logger.LogInformation("Got {count} status messages from IoT hub", message?.Records.Count() ?? 0);
//Did we get anything
if (message == null)
//No, abort here
return;
//Update the statuscode in the database
//[...]
//Complete all the received messages
var cancellationTokenSource = new CancellationTokenSource();
await feedbackReceiver.CompleteAsync(message, cancellationTokenSource.Token);
The issue
The function that sends the message to the IoT hub has the following code.
var message = new Message(Encoding.ASCII.GetBytes(JsonSerializer.Serialize(commands)))
{
Ack = DeliveryAcknowledgement.Full,
MessageId = command.Id,
};
await _iotHubServiceClient.SendAsync(deviceId, message);
So, when the front-end now inserts a new document into the command container the message is correctly transmitted to the device (via MQTT). It seems that the command get's "stuck" somewhere in transmission. My device keeps on receiving duplicate MQTT messages that have been received already.
My timed function also had this issue. It keeps on getting the acknowledgements for the same message over and over.
I would really like to have full acknowledgement sine this is the only way for the operators to get (delayed) feedback.
Environment
Hosting platform: Azure function app, .NET 8 (LTS), isolated worker model, windows. Runtime version 4.30.0.22097
Microsoft Visual Studio Professional 2022 (64-bit) Version 17.9.2