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
orfor
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 forDeviceClient
andModuleClient
. 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
orAmqp_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 bothPositive
andNegative
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
returnsnull
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 toCompleteAsync
deletes the specified sent message from the message queue based on theTask
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 benull
.context
- An optional context of typeobject
. Usenull
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
orMQTT_WS
cannotABANDON
orREJECT
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
Service client sample - Send message example, #1.
Service client sample - Send message example, #2.
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 IoTHubDeviceClient
to 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 typestr
(string).properties
- An optional collection of properties of typedict
. 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 usingHttp
, theClient
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';
Link the message feedback receiver
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.