Complete/Reject/Abandon – Part 2 of MKR1000 Azure IoT Hub Interface Using HTTP

Azure IoT cloud-to-device message lifecycle

In the earlier post MKR1000 Azure IoT Hub Interface using HTTP, I wrote about how to get the MKR1000 ready for Azure IoT Hub and start receiving data from Azure IoT. You might have noticed the following when running the sketch in the earlier post: After using the Device Explorer to send a single message, the MKR1000 would have blinked its LED after the first receive. If you continue to let the sketch run with out sending any new message, after about 60 seconds you will notice that MKR1000 receiving the same message and blinking again. This is due to the way a message’s life cycle works on the Azure IoT Hub. In this post I will address how the devices should send any one of Complete/Reject/Abandon message back to the Azure IoT Hub so that the message is not replayed again. This is essential if you are to interface with Azure IoT Hub correctly with HTTP.

Understanding the “cloud-to-device” message life cycle

Azure IoT is conceptually comprised of two distinct channels. A “cloud-to-device” channel and a “device-to-cloud” channel. Each channel is a queue. First in First out. A device “receives” communications from the “cloud-to-device” channel. During the time that the message is in the queue, several state changes govern the message interaction between Azure and the consuming device.

Azure IoT cloud-to-device message lifecycle
Azure IoT cloud-to-device message lifecycle (Source: Microsoft)
  1. Once a message is en-queued on the per-device “cloud-to-device” channel, it remains in en-queued status until a device “receives” the message. In case of AMQP/MQTT, the message will be “pushed” to the devices once they register for the channel and subscribe. In case of HTTP, the message will be “pulled” when ever a GET operation is performed on the endpoint. In the last blog post mentioned above, this HTTP pull was performed.
  2.  Once a message is pulled/pushed, Azure IoT Hub will set the message status to “invisible” or “locked” status freeing up the other messages behind it in the queue to be “received”. My understanding is that this default lock time is 1 minute. Once the default lock expires, the message will be en-queued again. This is why in the earlier example, the device received the message again. So its essential to ensure the device sends out any one of the following three to avoid repetitions.
  3. If the device processes the message and decides no more processing of the message is necessary it can set the status as “Complete” by calling the appropriate endpoint listed above.
  4. If the device explicitly “abandons” the message or if the lock expires, the message will go back to the “en-queued” status.
  5. If the device explicitly “rejects” the message, it will go to the “dead-lettered” status and won’t be available for enqueing any longer.
  6. Also when the message expires or the number of delivery count exceeds and the message has still not been “completed” the message will be “deadlettered”.

So in order to have your devices interfaced correctly with the Azure IoT Hub, the message life cycle should be handled correctly.

Azure IoT Hub Complete/Reject/Abandon HTTP endpoints

Having pulled the message in the earlier post, we will see how to “complete” or “reject” or “abandon” a message using the following HTTP endpoints.

Purpose
HTTP Verb
URI
“Complete” a received message. DELETE
https://{IoTHubName}.azure-devices.net/devices/{deviceId}/messages/devicebound/{message lock id, from the received etag}?api-version={api-version}
“Reject” a received message
DELETE https://{IoTHubName}.azure-devices.net/devices/{deviceId}/messages/devicebound/{message lock id, from the received etag}?reject&api-version={api-version}
“Abandon” a received message
POST https://{IoTHubName}.azure-devices.net/devices/{deviceId}/messages/devicebound/{message lock id, from the received etag}?abandon&api-version={api-version}

The only new parameter that’s needed for these endpoints is the “message lock id”. This is available as ETag in the HTTP Header of the received message.

MKR1000 Updated Arduino Sketch including Complete/Reject/Abandon

The WiFi Network, Azure and SAS token configurations were as explained in the earlier post. Following are the URIs for Receive, Complete, Reject and Abandon messages. Replace the device name. The “etag” string will replaced on the fly.

//message receive URI
String azureReceive = "/devices/MyMKR1000/messages/devicebound?api-version=2016-02-03"; 

// message Complete/Reject/Abandon URIs.  "etag" will be replaced with the message id E-Tag recieved from recieve call.
String azureComplete = "/devices/MyMKR1000/messages/devicebound/etag?api-version=2016-02-03";         
String azureReject   = "/devices/MyMKR1000/messages/devicebound/etag?reject&api-version=2016-02-03";  
String azureAbandon  = "/devices/MyMKR1000/messages/devicebound/etag/abandon?&api-version=2016-02-03"; 

The following functions take care of performing the Azure IoT Receive, Complete, Reject and Abandon HTTP requests. All the HTTP client related code is encapsulated within the httpRequest function.

//Receive Azure IoT Hub "cloud-to-device" message
void azureIoTReceiveMessage()
{
  httpRequest("GET", azureReceive, "","");
}

//Tells Azure IoT Hub that the message with the msgLockId is handled and it can be removed from the queue.
void azureIoTCompleteMessage(String eTag)
{
  String uri=azureComplete;
  uri.replace("etag",trimETag(eTag));

  httpRequest("DELETE", uri,"","");
}


//Tells Azure IoT Hub that the message with the msgLockId is rejected and can be moved to the deadletter queue
void azureIoTRejectMessage(String eTag)
{
  String uri=azureReject;
  uri.replace("etag",trimETag(eTag));

  httpRequest("DELETE", uri,"","");
}

//Tells Azure IoT Hub that the message with the msgLockId is abondoned and can be requeued.
void azureIoTAbandonMessage(String eTag)
{
  String uri=azureAbandon;
  uri.replace("etag",trimETag(eTag));

  httpRequest("POST", uri,"text/plain","");
}


void httpRequest(String verb, String uri,String contentType, String content)
{
    if(verb.equals("")) return;
    if(uri.equals("")) return;

    // close any connection before send a new request.
    // This will free the socket on the WiFi shield
    client.stop();
  
    // if there's a successful connection:
    if (client.connect(hostname, 443)) {
      client.print(verb); //send POST, GET or DELETE
      client.print(" ");  
      client.print(uri);  // any of the URI
      client.println(" HTTP/1.1"); 
      client.print("Host: "); 
      client.println(hostname);  //with hostname header
      client.print("Authorization: ");
      client.println(authSAS);  //Authorization SAS token obtained from Azure IoT device explorer
      client.println("Connection: close");

      if(verb.equals("POST"))
      {
          client.print("Content-Type: ");
          client.println(contentType);
          client.print("Content-Length: ");
          client.println(content.length());
          client.println();
          client.println(content);
      }else
      {
          client.println();
      }
    }
}

After receiving the message retrieve the ETag and either complete or reject or abandon the message.

     //first get the ETag from the received message response 
      String eTag=getHeaderValue(response,"ETag");

      //Uncomment the following line and comment out Reject and Abandon calls to verify Complete
      azureIoTCompleteMessage(eTag);

      //Uncomment the following line and comment out Complete and Abandon calls to verify Reject
      //azureIoTRejectMessage(eTag);

      //Uncomment the following line and comment out Complete and Reject calls to verify Abandon
      //azureIoTAbandonMessage(eTag); 

Here is a screen shot from the Device Explorer with the “Monitor Feedback Endpoint” checkbox checked to show how sending the “Complete” and “Reject” request created respective positive and negative acknowledgment with in Azure IoT.

Azure IoT Complete cloud-to-device message
Azure IoT Complete cloud-to-device message
Azure IoT Reject cloud-to-device message
Azure IoT Reject cloud-to-device message

By the way, When you do Abandon, you will notice that the same message getting retrieved every time the Receive http request is made.

The full source code for the sketch is available in GitHub as MKR1000AzureReceiveFull.ino.

This concludes this entire life cycle of a Azure IoT Hub “cloud-to-device” message. In the next part we will see about posting a “device-to-cloud” message with data payload to the Azure IoT Hub.

2 thoughts on “Complete/Reject/Abandon – Part 2 of MKR1000 Azure IoT Hub Interface Using HTTP”

  1. Hey Mohan,

    Thanks a lot for the article!

    I was tracking the time it took to run the program and it turns out that client.connect(hostname, 443) step in HttpRequest function takes about 4-5 seconds.

    If I send a message to Azure through my IoTHub Explorer (I’m using Mac OS), I want the Arduino MKR1000 to be able to respond right away and turn the LED on. I eventually want to remote control a motor connected to MKR1000 through Azure IoT Hub, but the 4 seconds respond time won’t work for this.

    Could you think of a way to make the respond faster?

    Genie

Leave a Reply

Your email address will not be published. Required fields are marked *