Редагувати

Поділитися через


Send and receive cloud-to-device messages

Azure IoT Hub is a fully managed service that enables bi-directional communications, including cloud-to-device (C2D) messages from solution back ends to millions of devices.

This article describes how to use the Azure IoT SDKs to build the following types of applications:

  • Device applications that receive and handle cloud-to-device messages from an IoT Hub messaging queue.

  • Back end applications that send cloud-to-device messages to a single device through an IoT Hub messaging queue.

This article is meant to complement runnable SDK samples that are referenced from within this article.

Note

The features described in this article are available only in the standard tier of IoT Hub. For more information about the basic and standard/free IoT Hub tiers, see Choose the right IoT Hub tier for your solution.

Overview

For a device application to receive cloud-to-device messages, it must connect to IoT Hub and then set up a message handler to process incoming messages. The Azure IoT Hub device SDKs provide classes and methods that a device can use to receive and handle messages from the service. This article discusses key elements of any device application that receives messages, including:

  • Declare a device client object
  • Connect to IoT Hub
  • Retrieve messages from the IoT Hub message queue
  • Process the message and send an acknowledgment back to IoT Hub
  • Configure a receive message retry policy

For a back end application to send cloud-to-device messages, it must connect to an IoT Hub and send messages through an IoT Hub message queue. The Azure IoT Hub service SDKs provide classes and methods that an application can use to send messages to devices. This article discusses key elements of any application that sends messages to devices, including:

  • Declare a service client object
  • Connect to IoT Hub
  • Build and send the message
  • Receive delivery feedback
  • Configure a send message retry policy

Understand the message queue

To understand cloud-to-device messaging, it's important to understand some fundamentals about how IoT Hub device message queues work.

Cloud-to-device messages sent from a solution backend application to an IoT device are routed through IoT Hub. There's no direct peer-to-peer messaging communication between the solution backend application and the target device. IoT Hub places incoming messages into its message queue, ready to be downloaded by target IoT devices.

To guarantee at-least-once message delivery, IoT hub persists cloud-to-device messages in per-device queues. Devices must explicitly acknowledge completion of a message before IoT Hub removes the message from the queue. This approach guarantees resiliency against connectivity and device failures.

When IoT Hub puts a message in a device message queue, it sets the message state to Enqueued. When a device thread takes a message from the queue, IoT Hub locks the message by setting the message state to Invisible. This state prevents other threads on the device from processing the same message. When a device thread successfully completes the processing of a message, it notifies IoT Hub and then IoT Hub sets the message state to Completed.

A device application that successfully receives and processes a message is said to Complete the message. However, if necessary a device can also:

  • Reject the message, which causes IoT Hub to set it to the Dead lettered state. Devices that connect over the Message Queuing Telemetry Transport (MQTT) protocol can't reject cloud-to-device messages.
  • Abandon the message, which causes IoT Hub to put the message back in the queue, with the message state set to Enqueued. Devices that connect over the MQTT protocol can't abandon cloud-to-device messages.

For more information about the cloud-to-device message lifecycle and how IoT Hub processes cloud-to-device messages, see Send cloud-to-device messages from an IoT hub.

Receive cloud-to-device messages

This section describes how to receive cloud-to-device messages using the DeviceClient class in the Azure IoT SDK for .NET.

There are two options that a device client application can use to receive messages:

  • Polling: The device application checks for new IoT Hub messages using a code loop (for example, a while or for loop). The loop executes continually, checking for messages.
  • Callback: The device application sets up an asynchronous message handler method that is called immediately when a message arrives.

Declare a DeviceClient object

DeviceClient includes methods and properties necessary to receive messages from IoT Hub.

For example:

static DeviceClient deviceClient;

Supply the connection parameters

Supply the IoT Hub primary connection string and Device ID to DeviceClient using the CreateFromConnectionString method. In addition to the required IoT Hub primary connection string, the CreateFromConnectionString method can be overloaded to include these optional parameters:

  • transportType - The transport protocol: variations of HTTP version 1, AMQP, or MQTT. AMQP is the default. To see all available values, see TransportType Enum.
  • transportSettings - Interface used to define various transport-specific settings for DeviceClient and ModuleClient. For more information, see ITransportSettings Interface.
  • ClientOptions - Options that allow configuration of the device or module client instance during initialization.

This example calls CreateFromConnectionString to define the DeviceClient connection IoT hub and device ID settings.

static string connectionString = "{your IoT hub connection string}";
static string deviceID = "{your device ID}";
deviceClient = DeviceClient.CreateFromConnectionString(connectionString,deviceID);

Polling

Polling uses ReceiveAsync to check for messages.

A call to ReceiveAsync can take these forms:

  • ReceiveAsync() - Wait for the default timeout period for a message before continuing.
  • ReceiveAsync (Timespan) - Receive a message from the device queue using a specific timeout.
  • ReceiveAsync (CancellationToken) - Receive a message from the device queue using a cancellation token. When using a cancellation token, the default timeout period is not used.

When using a transport type of HTTP 1 instead of MQTT or AMQP, the ReceiveAsync method returns immediately. The supported pattern for cloud-to-device messages with HTTP 1 is intermittently connected devices that check for messages infrequently (a minimum of every 25 minutes). Issuing more HTTP 1 receives results in IoT Hub throttling the requests. For more information about the differences between MQTT, AMQP, and HTTP 1 support, see Cloud-to-device communications guidance and Choose a communication protocol.

CompleteAsync method

After the device receives a message, the device application calls the CompleteAsync method to notify IoT Hub that the message is successfully processed and that the message can be safely removed from the IoT Hub device queue. The device should call this method when its processing successfully completes regardless of the transport protocol it's using.

Message abandon, reject, or timeout

With AMQP and HTTP version 1 protocols, but not the MQTT protocol, the device can also:

  • Abandon a message by calling AbandonAsync. This results in IoT Hub retaining the message in the device queue for future consumption.
  • Reject a message by calling RejectAsync. This permanently removes the message from the device queue.

If something happens that prevents the device from completing, abandoning, or rejecting the message, IoT Hub will, after a fixed timeout period, queue the message for delivery again. For this reason, the message processing logic in the device app must be idempotent, so that receiving the same message multiple times produces the same result.

For more information about the cloud-to-device message lifecycle and how IoT Hub processes cloud-to-device messages, see Send cloud-to-device messages from an IoT hub.

Polling loop

Using polling, an application uses a code loop that calls the ReceiveAsync method repeatedly to check for new messages until stopped.

If using ReceiveAsync with a timeout value or the default timeout, in the loop each call to ReceiveAsync waits for the specified timeout period. If ReceiveAsync times out, a null value is returned and the loop continues.

When a message is received, a Task object is returned by ReceiveAsync that should be passed to CompleteAsync. A call to CompleteAsync notifies IoT Hub to delete the specified message from the message queue based on the Task parameter.

In this example, the loop calls ReceiveAsync until a message is received or the polling loop is stopped.

static bool stopPolling = false;

while (!stopPolling)
{
   // Check for a message. Wait for the default DeviceClient timeout period.
   using Message receivedMessage = await _deviceClient.ReceiveAsync();

   // Continue if no message was received
   if (receivedMessage == null)
   {
      continue;
   }
   else  // A message was received
   {
      // Print the message received
      Console.WriteLine($"{DateTime.Now}> Polling using ReceiveAsync() - received message with Id={receivedMessage.MessageId}");
      PrintMessage(receivedMessage);

      // Notify IoT Hub that the message was received. IoT Hub will delete the message from the message queue.
      await _deviceClient.CompleteAsync(receivedMessage);
      Console.WriteLine($"{DateTime.Now}> Completed C2D message with Id={receivedMessage.MessageId}.");
   }

   // Check to see if polling loop should end
   stopPolling = ShouldPollingstop ();
}

Callback

To receive callback cloud-to-device messages in the device application, the application must connect to the IoT Hub and set up a callback listener to process incoming messages. Incoming messages to the device are received from the IoT Hub message queue.

Using callback, the device application sets up a message handler method using SetReceiveMessageHandlerAsync. The message handler is called then a message is received. Creating a callback method to receive messages removes the need to continuously poll for received messages.

Callback is available only using these protocols:

  • Mqtt
  • Mqtt_WebSocket_Only
  • Mqtt_Tcp_Only
  • Amqp
  • Amqp_WebSocket_Only
  • Amqp_Tcp_only

The Http1 protocol option does not support callbacks since the SDK methods would need to poll for received messages anyway, which defeats the callback principle.

In this example, SetReceiveMessageHandlerAsync sets up a callback handler method named OnC2dMessageReceivedAsync, which is called each time a message is received.

// Subscribe to receive C2D messages through a callback (which isn't supported over HTTP).
await deviceClient.SetReceiveMessageHandlerAsync(OnC2dMessageReceivedAsync, deviceClient);
Console.WriteLine($"\n{DateTime.Now}> Subscribed to receive C2D messages over callback.");

Receive message retry policy

The device client message retry policy can be defined using DeviceClient.SetRetryPolicy.

The message retry timeout is stored in the DeviceClient.OperationTimeoutInMilliseconds property.

SDK receive message sample

The .NET/C# SDK includes a Message Receive sample that includes the receive message methods described in this section.

Send cloud-to-device messages

This section describes essential code to send a message from a solution backend application to an IoT device using the ServiceClient class in the Azure IoT SDK for .NET. As discussed previously, a solution backend application connects to an IoT Hub and messages are sent to IoT Hub encoded with a destination device. IoT Hub stores incoming messages to its message queue, and messages are delivered from the IoT Hub message queue to the target device.

A solution backend application can also request and receive delivery feedback for a message sent to IoT Hub that is destined for device delivery via the message queue.

Declare a ServiceClient object

ServiceClient includes methods and properties necessary to send messages from an application through IoT Hub to a device.

static ServiceClient serviceClient;

Supply the connection string

Supply the IoT Hub primary connection string to ServiceClient using the CreateFromConnectionString method. In addition to the required IoT Hub primary connection string, the CreateFromConnectionString method can be overloaded to include these optional parameters:

  • transportType - Amqp or Amqp_WebSocket_Only.
  • transportSettings - The AMQP and HTTP proxy settings for Service Client.
  • ServiceClientOptions - Options that allow configuration of the service client instance during initialization. For more information, see ServiceClientOptions.

This example creates the ServiceClient object using the IoT Hub connection string.

static string connectionString = "{your iot hub connection string}";
serviceClient = ServiceClient.CreateFromConnectionString(connectionString);

Send an asynchronous cloud-to-device message

Use sendAsync to send an asynchronous message from an application through the cloud (IoT Hub) to the device. The call is made using the AMQP protocol.

sendAsync uses these parameters:

  • deviceID - The string identifier of the target device.
  • message - The cloud-to-device message. The message is of type Message and can be formatted accordingly.
  • timeout - An optional timeout value. The default is one minute if unspecified.

This example sends a test message to the target device with 10-second timeout value.

string targetDevice = "Device-1";
static readonly TimeSpan operationTimeout = TimeSpan.FromSeconds(10);
var commandMessage = new
Message(Encoding.ASCII.GetBytes("Cloud to device message."));
await serviceClient.SendAsync(targetDevice, commandMessage, operationTimeout);

Receive delivery feedback

A sending program can request delivery (or expiration) acknowledgments from IoT Hub for each cloud-to-device message. This option enables the sending program to use inform, retry, or compensation logic. A complete description of message feedback operations and properties are described at Message feedback.

To receive message delivery feedback:

  • Create the feedbackReceiver object
  • Send messages using the Ack parameter
  • Wait to receive feedback

Create the feedbackReceiver object

Call GetFeedbackReceiver to create a FeedbackReceiver object. FeedbackReceiver contains methods that services can use to perform feedback receive operations.

var feedbackReceiver = serviceClient.GetFeedbackReceiver();

Send messages using the Ack parameter

Each message must include a value for the delivery acknowledgment Ack property in order to receive delivery feedback. The Ack property can be one of these values:

  • none (default): no feedback message is generated.

  • Positive: receive a feedback message if the message was completed.

  • Negative: receive a feedback message if the message expired (or maximum delivery count was reached) without being completed by the device.

  • Full: feedback for both Positive and Negative results.

In this example, the Ack property is set to Full, requesting both positive or negative message delivery feedback for one message.

var commandMessage = new
Message(Encoding.ASCII.GetBytes("Cloud to device message."));
commandMessage.Ack = DeliveryAcknowledgement.Full;
await serviceClient.SendAsync(targetDevice, commandMessage);

Wait to receive feedback

Define a CancellationToken. Then in a loop, call ReceiveAsync repeatedly, checking for delivery feedback messages. Each call to ReceiveAsync waits for the timeout period defined for the ServiceClient object.

  • If a ReceiveAsync timeout expires with no message received, ReceiveAsync returns null and the loop continues.
  • If a feedback message is received, a Task object is returned by ReceiveAsync that should be passed to CompleteAsync along with the cancellation token. A call to CompleteAsync deletes the specified sent message from the message queue based on the Task parameter.
  • If needed, the receive code can call AbandonAsync to put a send message back in the queue.
var feedbackReceiver = serviceClient.GetFeedbackReceiver();
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
// Call ReceiveAsync, passing the token. Wait for the timout period.
var feedbackBatch = await feedbackReceiver.ReceiveAsync(token);
if (feedbackBatch == null) continue;

This example shows a method that includes these steps.

private async static void ReceiveFeedbackAsync()
{
      var feedbackReceiver = serviceClient.GetFeedbackReceiver();

      Console.WriteLine("\nReceiving c2d feedback from service");
      while (true)
      {
         // Check for messages, wait for the timeout period.
         var feedbackBatch = await feedbackReceiver.ReceiveAsync();
         // Continue the loop if null is received after a timeout.
         if (feedbackBatch == null) continue;

         Console.ForegroundColor = ConsoleColor.Yellow;
         Console.WriteLine("Received feedback: {0}",
            string.Join(", ", feedbackBatch.Records.Select(f => f.StatusCode)));
         Console.ResetColor();

         await feedbackReceiver.CompleteAsync(feedbackBatch);
      }
   }

Note this feedback receive pattern is similar to the pattern used to receive cloud-to-device messages in the device application.

Service client reconnection

On encountering an exception, the service client relays that information to the calling application. At that point, it is recommended that you inspect the exception details and take necessary action.

For example:

  • If it a network exception, you can retry the operation.
  • If it is a security exception (unauthorized exception), inspect your credentials and make sure they are up-to-date.
  • If it is a throttling/quota exceeded exception, monitor and/or modify the frequency of sending requests, or update your hub instance scale unit. See IoT Hub quotas and throttling for details.

Send message retry policy

The ServiceClient message retry policy can be defined using ServiceClient.SetRetryPolicy.

SDK send message sample

The .NET/C# SDK includes a Service client sample that includes the send message methods described in this section.

Receive cloud-to-device messages

This section describes how to receive cloud-to-device messages using the DeviceClient class from the Azure IoT SDK for Java.

For a Java-based device application to receive cloud-to-device messages, it must connect to IoT Hub, then set up a callback listener and message handler to process incoming messages from IoT Hub. The device application should also be able to detect and handle disconnects in case the device-to-IoT Hub message connection is broken.

Import Azure IoT Java SDK libraries

The code referenced in this article uses these SDK libraries.

import com.microsoft.azure.sdk.iot.device.*;
import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException;
import com.microsoft.azure.sdk.iot.device.transport.IotHubConnectionStatus;

Declare a DeviceClient object

The DeviceClient object instantiation requires these parameters:

  • connString - The IoT device connection string. The connection string is a set of key-value pairs that are separated by ';', with the keys and values separated by '='. It should contain values for these keys: HostName, DeviceId, and SharedAccessKey.
  • Transport protocol - The DeviceClient connection can use one of the following IoTHubClientProtocol transport protocols. AMQP is the most versatile, allows for checking messages frequently, and allows for message rejection and cancel. MQTT doesn't support message rejection or abandon methods:
    • AMQPS
    • AMQPS_WS
    • HTTPS
    • MQTT
    • MQTT_WS

For example:

static string connectionString = "{a device connection string}";
static protocol = IotHubClientProtocol.AMQPS;
DeviceClient client = new DeviceClient(connectionString, protocol);

Set the message callback method

Use the setMessageCallback method to define a message handler method that is notified when a message is received from IoT Hub.

setMessageCallback includes these parameters:

  • callback - The callback method name. Can be null.
  • context - An optional context of type object. Use null if unspecified.

In this example, a callback method named MessageCallback with no context parameter is passed to setMessageCallback.

client.setMessageCallback(new MessageCallback(), null);

Create a message callback handler

A callback message handler receives and processes an incoming message passed from the IoT Hub messages queue.

In this example, the message handler processes an incoming message and then returns IotHubMessageResult.COMPLETE. A IotHubMessageResult.COMPLETE return value notifies IoT Hub that the message is successfully processed and that the message can be safely removed from the device queue. The device should return IotHubMessageResult.COMPLETE when its processing successfully completes, notifying IoT Hub that the message should be removed from the message queue, regardless of the protocol it's using.

  protected static class MessageCallback implements com.microsoft.azure.sdk.iot.device.MessageCallback
  {
      public IotHubMessageResult onCloudToDeviceMessageReceived(Message msg, Object context)
      {
          System.out.println(
                  "Received message with content: " + new String(msg.getBytes(), Message.DEFAULT_IOTHUB_MESSAGE_CHARSET));
          // Notify IoT Hub that the message
          return IotHubMessageResult.COMPLETE;
      }
  }

Message abandon and rejection options

Though the vast number of incoming messages to a device should be successfully received and result in IotHubMessageResult.COMPLETE, it may be necessary to abandon or reject a message.

  • With AMQP and HTTPS, but not MQTT, an application can:
    • IotHubMessageResult.ABANDON the message. IoT hub requeues it and sends it again later.
    • IotHubMessageResult.REJECT the message. IoT hub doesn't requeue the message and permanently removes the message from the message queue.
  • Clients using MQTT or MQTT_WS cannot ABANDON or REJECT messages.

If something happens that prevents the device from completing, abandoning, or rejecting the message, IoT Hub will, after a fixed timeout period, queue the message for delivery again. For this reason, the message processing logic in the device app must be idempotent, so that receiving the same message multiple times produces the same result.

For more information about the cloud-to-device message lifecycle and how IoT Hub processes cloud-to-device messages, see Send cloud-to-device messages from an IoT hub.

Note

If you use HTTPS instead of MQTT or AMQP as the transport, the DeviceClient instance checks for messages from IoT Hub infrequently (a minimum of every 25 minutes). For more information about the differences between MQTT, AMQP, and HTTPS support, see Cloud-to-device communications guidance and Choose a communication protocol.

Create the message state callback method

An application can use registerConnectionStatusChangeCallback to register a callback method to be executed when the connection status of the device changes. This way the application can detect a downed messages connection and attempt to reconnect.

In this example, IotHubConnectionStatusChangeCallbackLogger is registered as the connection status change callback method.

client.registerConnectionStatusChangeCallback(new IotHubConnectionStatusChangeCallbackLogger(), new Object());

The callback is fired and passed a ConnectionStatusChangeContext object.

Call connectionStatusChangeContext.getNewStatus() to get the current connection state.

IotHubConnectionStatus status = connectionStatusChangeContext.getNewStatus();

The connection state returned can be one of these values:

  • IotHubConnectionStatus.DISCONNECTED
  • IotHubConnectionStatus.DISCONNECTED_RETRYING
  • IotHubConnectionStatus.CONNECTED

Call connectionStatusChangeContext.getNewStatusReason() to get the reason for the connection status change.

IotHubConnectionStatusChangeReason statusChangeReason = connectionStatusChangeContext.getNewStatusReason();

Call connectionStatusChangeContext.getCause() to find the reason for the connection status change. getCause() may return null if no information is available.

Throwable throwable = connectionStatusChangeContext.getCause();
if (throwable != null)
    throwable.printStackTrace();

See the HandleMessages sample listed in the SDK receive message sample section of this article for a complete sample showing how to extract the status change callback method connection status change status, reason why the device status changed, and context.

Open the connection between device and IoT Hub

Use open to create a connection between the device and IoT Hub. The device can now asynchronously send and receive messages to and from an IoT Hub. If the client is already open, the method does nothing.

client.open(true);

SDK receive message sample

HandleMessages: a sample device app included with the Microsoft Azure IoT SDK for Java, which connects to your IoT hub and receives cloud-to-device messages.

Send cloud-to-device messages

This section describes how to send a cloud-to-device message using the ServiceClient class from the Azure IoT SDK for Java. A solution backend application connects to an IoT Hub and messages are sent to IoT Hub encoded with a destination device. IoT Hub stores incoming messages to its message queue, and messages are delivered from the IoT Hub message queue to the target device.

A solution backend application can also request and receive delivery feedback for a message sent to IoT Hub that is destined for device delivery via the message queue.

Add the dependency statement

Add the dependency to use the iothub-java-service-client package in your application to communicate with your IoT hub service:

<dependency>
  <groupId>com.microsoft.azure.sdk.iot</groupId>
  <artifactId>iot-service-client</artifactId>
  <version>1.7.23</version>
</dependency>

Add import statements

Add these import statements to use the Azure IoT Java SDK and exception handler.

import com.microsoft.azure.sdk.iot.service.*;
import java.io.IOException;
import java.net.URISyntaxException;

Define the connection protocol

Use IotHubServiceClientProtocol to define the application-layer protocol used by the service client to communicate with an IoT Hub.

IotHubServiceClientProtocol only accepts the AMQPS or AMQPS_WS enum.

private static final IotHubServiceClientProtocol protocol =    
    IotHubServiceClientProtocol.AMQPS;

Create the ServiceClient object

Create the ServiceClient object, supplying the Iot Hub connection string and protocol.

private static final String connectionString = "{yourhubconnectionstring}";
private static final ServiceClient serviceClient (connectionString, protocol);

Open the connection between application and IoT Hub

open the AMQP sender connection. This method creates the connection between the application and IoT Hub.

serviceClient.open();

Open a feedback receiver for message delivery feedback

You can use a FeedbackReceiver to get sent message delivery to IoT Hub feedback. A FeedbackReceiver is a specialized receiver whose Receive method returns a FeedbackBatch instead of a Message.

In this example, the FeedbackReceiver object is created and the open() statement is called to await feedback.

FeedbackReceiver feedbackReceiver = serviceClient
  .getFeedbackReceiver();
if (feedbackReceiver != null) feedbackReceiver.open();

Add message properties

You can optionally use setProperties to add message properties. These properties are included in the message sent to the device and can be extracted by the device application upon receipt.

Map<String, String> propertiesToSend = new HashMap<String, String>();
propertiesToSend.put(messagePropertyKey,messagePropertyKey);
messageToSend.setProperties(propertiesToSend);

Create and send an asynchronous message

The Message object stores the message to be sent. In this example, a "Cloud to device message" is delivered.

Use setDeliveryAcknowledgement to request delivered/not delivered to IoT Hub message queue acknowledgment. In this example, the acknowledgment requested is Full, either delivered or not delivered.

Use SendAsync to send an asynchronous message from the client to the device. Alternatively, you can use the Send (not async) method, but this function is synchronized internally so that only one send operation is allowed at a time. The message is delivered from the application to IoT Hub. IoT Hub puts the message into the message queue, ready to be delivered to the target device.

Message messageToSend = new Message("Cloud to device message.");
messageToSend.setDeliveryAcknowledgementFinal(DeliveryAcknowledgement.Full);
serviceClient.sendAsync(deviceId, messageToSend);

Receive message delivery feedback

After a message is sent from the application, the application can call receive with or without a timeout value. If a timeout value is not supplied, the default timeout is used. This passes back a FeedbackBatch object that contains message delivery feedback properties that can be examined.

This example creates the FeedbackBatch receiver and calls getEnqueuedTimeUtc, printing the message enqueued time.

FeedbackBatch feedbackBatch = feedbackReceiver.receive(10000);
if (feedbackBatch != null) {
  System.out.println("Message feedback received, feedback time: "
    + feedbackBatch.getEnqueuedTimeUtc().toString());
}

SDK send message samples

Install the Azure IoT SDK library

Install the azure-iot-device SDK library on your development machine before calling any related code:

pip install azure-iot-device

There are two Python SDK classes that are used to send messages to and from IoT devices. Message handling methods from these classes are described in sections on this page.

  • The IoTHubDeviceClient class includes methods to create a synchronous connection from a device to an Azure IoT Hub and receive messages from IoT Hub.

  • The IoTHubRegistryManager class includes APIs for IoT Hub Registry Manager operations. In this article, methods from this class show how to connect to IoT Hub and send a message to a device.

Receive cloud-to-device messages

This section describes how to receive cloud-to-device messages using the IoTHubDeviceClient class from the Azure IoT SDK for Python.

For a Python-based device application to receive cloud-to-device messages, it must connect to IoT Hub and then set up a callback message handler to process incoming messages from IoT Hub.

Import the IoTHubDeviceClient object

Add a line of code to import the IoTHubDeviceClient functions from the azure.iot.device SDK.

from azure.iot.device import IoTHubDeviceClient

Connect the device client

Instantiate the IoTHubDeviceClient, passing an IoT Hub connection string to create_from_connection_string. This creates a connection from the device to IoT Hub.

Alternatively, you can connect IoTHubDeviceClientto a device using one of these methods:

deviceConnectionString = "{your IoT hub connection string}";
client = IoTHubDeviceClient.create_from_connection_string ({deviceConnectionString})

Handle reconnection

IoTHubDeviceClient will by default attempt to reestablish a dropped connection. Reconnection behavior is governed by the IoTHubDeviceClient connection_retry and connection_retry_interval parameters.

Create a message handler

Create the message handler function to process incoming messages to the device.

In this example, message_handler is called when a message is received. The message properties (.items) are printed to the console using a loop.

def message_handler(message):
    global RECEIVED_MESSAGES
    RECEIVED_MESSAGES += 1
    print("")
    print("Message received:")

    # print data from both system and application (custom) properties
    for property in vars(message).items():
        print ("    {}".format(property))

    print("Total calls received: {}".format(RECEIVED_MESSAGES))

Assign the message handler

Use the on_message_received method to assign the message handler method to the IoTHubDeviceClient object.

In this example, a message handler method named message_handler is attached to the IoTHubDeviceClient client object. The client object waits to receive a cloud-to-device message from an IoT Hub. This code waits up to 300 seconds (5 minutes) for a message, or exits if a keyboard key is pressed.

try:
    # Attach the handler to the client
    client.on_message_received = message_handler

    while True:
        time.sleep(300)
except KeyboardInterrupt:
    print("IoT Hub C2D Messaging device sample stopped")
finally:
    # Graceful exit
    print("Shutting down IoT Hub Client")
    client.shutdown()

SDK receive message sample

Receive Message - Receive Cloud-to-Device (C2D) messages sent from the Azure IoT Hub to a device.

Send cloud-to-device messages

This section describes how to send a cloud-to-device message using the IoTHubRegistryManager class from the Azure IoT SDK for Python. A solution backend application connects to an IoT Hub and messages are sent to IoT Hub encoded with a destination device. IoT Hub stores incoming messages to its message queue, and messages are delivered from the IoT Hub message queue to the target device.

Import the IoTHubRegistryManager object

Add the following import statement. IoTHubRegistryManager includes APIs for IoT Hub Registry Manager operations.

from azure.iot.hub import IoTHubRegistryManager

Connect the IoT Hub registry manager

Instantiate the IoTHubRegistryManager object that connects to an IoT hub, passing an IoT Hub connection string to from_connection_string.

IoTHubConnectionString = "{Primary connection string to an IoT hub}"
registry_manager = IoTHubRegistryManager.from_connection_string(IoTHubConnectionString)

Build and send a message

Use send_c2d_message to send a message through the cloud (IoT Hub) to the device.

send_c2d_message uses these parameters:

  • deviceID - The string identifier of the target device.
  • message - The cloud-to-device message. The message is of type str (string).
  • properties - An optional collection of properties of type dict. Properties can contain application properties and system properties. The default value is {}.

This example sends a test message to the target device.

# define the device ID
deviceID = "Device-1"

# define the message
message = "{\"c2d test message\"}"

# include optional properties
props={}
props.update(messageId = "message1")
props.update(prop1 = "test property-1")
props.update(prop1 = "test property-2")
prop_text = "Test message"
props.update(testProperty = prop_text)

# send the message through the cloud (IoT Hub) to the device
registry_manager.send_c2d_message(deviceID, message, properties=props)

SDK send message sample

send_message.py - Demonstrates how to send a cloud-to-device message.

Install the Node.js messaging packages

Run these commands to install the azure-iot-device and azure-iothub packages on your development machine:

npm install azure-iot-device --save
npm install azure-iothub --save

The azure-iot-device package contains objects that interface with IoT devices. This article describes Client class code that receives messages from IoT Hub.

The azure-iothub package contains objects that interface with IoT Hub. This article describes Client class code that sends a message from an application to a device via IoT Hub.

Receive messages in the device application

This section describes how to receive cloud-to-device messages using the azure-iot-device package in the Azure IoT SDK for Node.js.

For a Node.js-based device application to receive cloud-to-device messages, it must connect to IoT Hub, then set up a callback listener and message handler to process incoming messages from IoT Hub. The device application should also be able to detect and handle disconnects in case the device-to-IoT Hub message connection is broken.

Create a client module

From the azure-iot-device package, create a Client using the Client class. The Client class contains methods that a device can use to receive from and send to IoT Hub.

const Client = require('azure-iot-device').Client;

Choose a transport protocol

The Client object supports these protocols:

  • Amqp
  • Http - When using Http, the Client instance checks for messages from IoT Hub infrequently (a minimum of every 25 minutes).
  • Mqtt
  • MqttWs
  • AmqpWs

For more information about the differences between MQTT, AMQP, and HTTPS support, see Cloud-to-device communications guidance and Choose a communication protocol.

This example assigns the AMQP protocol to a Protocol variable. This Protocol variable is passed to the Client.fromConnectionString method in the Add the connection string section of this article.

const Protocol = require('azure-iot-device-mqtt').Amqp;

Message completion, rejection, and abandon capabilities

Message completion, rejection, and abandon methods can be used depending on the protocol chosen.

AMQP and HTTP

The AMQP and HTTP transports can complete, reject, or abandon a message:

  • Complete - To complete a message, the service that sent the cloud-to-device message is notified that the message is received. IoT Hub removes the message from the message queue. The method takes the form of client.complete(message, callback function).
  • Reject - To reject a message, the service that sent the cloud-to-device message is notified that the message is not processed by the device. IoT Hub permanently removes the message from the device queue. The method takes the form of client.reject(message, callback function).
  • Abandon - To abandon a message, IoT Hub immediately tries to resend it. IoT Hub retains the message in the device queue for future consumption. The method takes the form of client.abandon(message, callback function).
MQTT

MQTT does not support message complete, reject, or abandon functions. Instead, MQTT accepts a message by default and the message is removed from the IoT Hub message queue.

Redelivery attempts

If something happens that prevents the device from completing, abandoning, or rejecting the message, IoT Hub will, after a fixed timeout period, queue the message for delivery again. For this reason, the message processing logic in the device app must be idempotent, so that receiving the same message multiple times produces the same result.

Add the IoT Hub string and transport protocol

Call fromConnectionString to establish a device-to-IoT hub connection using these parameters:

  • connStr - A connection string which encapsulates "device connect" permissions for an IoT hub. The connection string contains Hostname, Device ID & Shared Access Key in this format: "HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"
  • transportCtor - The transport protocol.
const Protocol = require('azure-iot-device-mqtt').Amqp;
let client = Client.fromConnectionString(deviceConnectionString, Protocol);

Create an incoming message handler

The message handler is called for each incoming message.

After a message is successfully received, if using AMQP or HTTP transport then call the client.complete method to inform IoT Hub that the message can be removed from the message queue.

For example, this message handler prints the message ID and message body to the console, then calls client.complete to notify IoT Hub that it processed the message and that it can safely be removed from the device queue. The call to complete isn't required if you're using MQTT transport and can be omitted. A call tocomplete is required for AMQP or HTTPS transport.

function messageHandler(msg) {
  console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);
  client.complete(msg, printResultFor('completed'));
}

Create a connection disconnect handler

The disconnect handler is called when the connection is disconnected. A disconnect handler is useful for implementing reconnect code.

This example catches and displays the disconnect error message to the console.

function disconnectHandler() {
  clearInterval(sendInterval);
  sendInterval = null;
  client.open().catch((err) => {
    console.error(err.message);
  });
}

Add event listeners

You can specify these event listeners using the .on method.

  • Connection handler
  • Error handler
  • Disconnect handler
  • Message handler

This example includes the message and disconnect handlers defined previously.

client.on('connect', connectHandler);
client.on('error', errorHandler);
client.on('disconnect', disconnectHandler);
client.on('message', messageHandler);

Open the connection to IoT Hub

Use the open method to open a connection between an IoT device and IoT Hub. Use .catch(err) to catch an error and call handler code.

For example:

client.open()
.catch((err) => {
  console.error('Could not connect: ' + err.message);
});

SDK receive message sample

simple_sample_device - A device app that connects to your IoT hub and receives cloud-to-device messages.

Send cloud-to-device messages

This section describes how to send a cloud-to-device message using the azure-iothub package from the Azure IoT SDK for Node.js. As discussed previously, a solution backend application connects to an IoT Hub and messages are sent to IoT Hub encoded with a destination device. IoT Hub stores incoming messages to its message queue, and messages are delivered from the IoT Hub message queue to the target device.

A solution backend application can also request and receive delivery feedback for a message sent to IoT Hub that is destined for device delivery via the message queue.

Load the client and message modules

Declare a Client object using the Client class from the azure-iothub package.

Declare a Message object using the Message class from the azure-iot-common package.

'use strict';
var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;

Create the Client object

Create the Client using fromConnectionString using these parameters:

  • IoT Hub connection string
  • Transport type

In this example, the serviceClient object is created with the Amqp transport type.

var connectionString = '{IoT Hub connection string}';
var serviceClient = Client.fromConnectionString(connectionString,`Amqp`);

Open the Client connection

Call the Client open method to open a connection between an application and IoT Hub.

open can be called with or without specifying a callback function that is called when the open operation is complete.

In this example, the open method includes an optional err open connection callback function. If an open error occurs, an error object is returned. If the open connection is successful, a null callback value is returned.

serviceClient.open(function (err)
if (err)
  console.error('Could not connect: ' + err.message);

Create a message

The message object includes the asynchronous cloud-to-device message. The message functionality works the same way over AMQP, MQTT, and HTTP.

The message object supports several properties, including these properties. See the message properties for a complete list.

  • ack - Delivery feedback. Described in the next section.
  • properties - A map containing string keys and values for storing custom message properties.
  • messageId - Used to correlate two-way communication.

Add the message body when the message object is instantiated. In this example, a 'Cloud to device message.' message is added.

var message = new Message('Cloud to device message.');
message.ack = 'full';
message.messageId = "My Message ID";

Delivery acknowledgment

A sending program can request delivery (or expiration) acknowledgments from IoT Hub for each cloud-to-device message. This option enables the sending program to use inform, retry, or compensation logic. A complete description of message feedback operations and properties are described at Message feedback.

Each message that is to receive message feedback must include a value for the delivery acknowledgment ack property. The ack property can be one of these values:

  • none (default): no feedback message is generated.

  • sent: receive a feedback message if the message was completed.

  • : receive a feedback message if the message expired (or maximum delivery count was reached) without being completed by the device.

  • full: feedback for both sent and not sent results.

In this example, the ack property is set to full, requesting both sent and not sent message delivery feedback for one message.

message.ack = 'full';

The message feedback receiver callback function is linked to the Client using getFeedbackReceiver.

The message feedback receiver receives two arguments:

  • Error object (can be null)
  • AmqpReceiver object - Emits events when new feedback messages are received by the client.

This example function receives and prints a delivery feedback message to the console.

function receiveFeedback(err, receiver){
  receiver.on('message', function (msg) {
    console.log('Feedback message:')
    console.log(msg.getData().toString('utf-8'));
  });
}

This code links the receiveFeedback feedback callback function to the service Client object using getFeedbackReceiver.

serviceClient.getFeedbackReceiver(receiveFeedback);

Define a message completion results handler

The message send completion callback function is called after each message is sent.

This example function prints message send operation results to the console. In this example, the printResultFor function is supplied as a parameter to the send function described in the next section.

function printResultFor(op) {
  return function printResult(err, res) {
    if (err) console.log(op + ' error: ' + err.toString());
    if (res) console.log(op + ' status: ' + res.constructor.name);
  };
}

Send a message

Use the send function to send an asynchronous cloud-to-device message to the device app through IoT Hub.

send supports these parameters:

  • deviceID - The device ID of the target device.
  • message - The body of the message to send to the device.
  • done - The optional function to call when the operation is complete. Done is called with two arguments:
    • Error object (can be null).
    • transport-specific response object useful for logging or debugging.

This code calls send to send a cloud-to-device message to the device app through IoT Hub. The callback function printResultFor defined in the previous section receives the delivery acknowledgment information.

var targetDevice = '{device ID}';
serviceClient.send(targetDevice, message, printResultFor('send'));

This example shows how to send a message to your device and handle the feedback message when the device acknowledges the cloud-to-device message:

serviceClient.open(function (err) {
  if (err) {
    console.error('Could not connect: ' + err.message);
  } else {
    console.log('Service client connected');
    serviceClient.getFeedbackReceiver(receiveFeedback);
    var message = new Message('Cloud to device message.');
    message.ack = 'full';
    message.messageId = "My Message ID";
    console.log('Sending message: ' + message.getData());
    serviceClient.send(targetDevice, message, printResultFor('send'));
  }
});

SDK send message sample

send_c2d_message.js - Send C2D messages to a device through IoT Hub.

Connection reconnection policy

This article doesn't demonstrate a message retry policy for the device to IoT Hub connection or external application to IoT Hub connection. In production code, you should implement connection retry policies as described in Manage device reconnections to create resilient applications.

Message retention time, retry attempts, and max delivery count

As described in Send cloud-to-device messages from IoT Hub, you can view and configure defaults for the following message values using portal IoT Hub configuration options or the Azure CLI. These configuration options can affect message delivery and feedback.

  • Default TTL (time to live) - The amount of time a message is available for a device to consume before it's expired by IoT Hub.
  • Feedback retention time - The amount of time IoT Hub retains the feedback for expiration or delivery of cloud-to-device messages.
  • The number of times IoT Hub attempts to deliver a cloud-to-device message to a device.