Send cloud-to-device messages with IoT Hub (Python)

Azure IoT Hub is a fully managed service that helps enable reliable and secure bi-directional communications between millions of devices and a solution back end. The Send telemetry from a device to an IoT hub quickstart shows how to create an IoT hub, provision a device identity in it, and code a simulated device app that sends device-to-cloud messages.

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 IoT Hub tiers, see Choose the right IoT Hub tier.

This tutorial builds on Send telemetry from a device to an IoT hub. It shows you how to:

  • From your solution back end, send cloud-to-device messages to a single device through IoT Hub.

  • Receive cloud-to-device messages on a device.

  • From your solution back end, request delivery acknowledgment (feedback) for messages sent to a device from IoT Hub.

You can find more information on cloud-to-device messages in the IoT Hub developer guide.

At the end of this tutorial, you run two Python console apps:

  • SimulatedDevice.py, a modified version of the app created in Send telemetry from a device to an IoT hub, which connects to your IoT hub and receives cloud-to-device messages.

  • SendCloudToDeviceMessage.py, which sends a cloud-to-device message to the simulated device app through IoT Hub, and then receives its delivery acknowledgment.

Note

IoT Hub has SDK support for many device platforms and languages (including C, Java, Javascript, and Python) through Azure IoT device SDKs. For instructions on how to use Python to connect your device to this tutorial's code, and generally to Azure IoT Hub, see the Azure IoT Python SDK.

Prerequisites

Receive messages in the simulated device app

In this section, you create a Python console app to simulate the device and receive cloud-to-device messages from the IoT hub.

  1. Using a text editor, create a SimulatedDevice.py file.

  2. Add the following import statements and variables at the start of the SimulatedDevice.py file:

     import threading
     from azure.iot.device import IoTHubDeviceClient
    
     RECEIVED_MESSAGES = 0
    
  3. Add the following code to SimulatedDevice.py file. Replace the "{deviceConnectionString}" placeholder value with the device connection string for the device you created in the Send telemetry from a device to an IoT hub quickstart:

    CONNECTION_STRING = "{deviceConnectionString}"
    
  4. Add the following function to print received messages to the console:

    def message_listener(client):
        global RECEIVED_MESSAGES
        while True:
            message = client.receive_message()
            RECEIVED_MESSAGES += 1
            print("Message received")
            print( "    Data: <<{}>>".format(message.data) )
            print( "    Properties: {}".format(message.custom_properties))
            print( "    Total calls received: {}".format(RECEIVED_MESSAGES))
    
  5. Add the following code to initialize the client and wait to receive the cloud-to-device message:

    def iothub_client_sample_run():
        try:
            client = iothub_client_init()
    
            message_listener_thread = threading.Thread(target=message_listener, args=(client,))
            message_listener_thread.daemon = True
            message_listener_thread.start()
    
            while True:
                time.sleep(1000)
    
        except KeyboardInterrupt:
            print ( "IoTHubDeviceClient sample stopped" )
    
  6. Add the following main function:

    if __name__ == '__main__':
        print ( "Starting the IoT Hub Python sample..." )
        print ( "IoTHubDeviceClient waiting for commands, press Ctrl-C to exit" )
    
        iothub_client_sample_run()
    
  7. Save and close SimulatedDevice.py file.

Get the IoT hub connection string

In this article you create a backend service to send cloud-to-device messages through the IoT hub you created in Send telemetry from a device to an IoT hub. To send cloud-to-device messages, your service needs the service connect permission. By default, every IoT Hub is created with a shared access policy named service that grants this permission.

To get the IoT Hub connection string for the service policy, follow these steps:

  1. In the Azure portal, select Resource groups. Select the resource group where your hub is located, and then select your hub from the list of resources.

  2. On the left-side pane of your IoT hub, select Shared access policies.

  3. From the list of policies, select the service policy.

  4. Under Shared access keys, select the copy icon for the Connection string -- primary key and save the value.

    Show how to retrieve the connection string

For more information about IoT Hub shared access policies and permissions, see Access control and permissions.

Send a cloud-to-device message

In this section, you create a Python console app that sends cloud-to-device messages to the simulated device app. You need the device ID of the device you added in the Send telemetry from a device to an IoT hub quickstart. You also need the the IoT hub connection string you copied previously in Get the IoT hub connection string.

  1. Using a text editor, create a SendCloudToDeviceMessage.py file.

  2. Add the following import statements and variables at the start of the SendCloudToDeviceMessage.py file:

    import random
    import sys
    import iothub_service_client
    from iothub_service_client import IoTHubMessaging, IoTHubMessage, IoTHubError
    
    OPEN_CONTEXT = 0
    FEEDBACK_CONTEXT = 1
    MESSAGE_COUNT = 1
    AVG_WIND_SPEED = 10.0
    MSG_TXT = "{\"service client sent a message\": %.2f}"
    
  3. Add the following code to SendCloudToDeviceMessage.py file. Replace the "{iot hub connection string}" and "{device id}" placeholder values with the IoT hub connection string and device ID you noted previously:

    CONNECTION_STRING = "{IoTHubConnectionString}"
    DEVICE_ID = "{deviceId}"
    
  4. Add the following function to print feedback messages to the console:

    def open_complete_callback(context):
        print ( 'open_complete_callback called with context: {0}'.format(context) )
    
    def send_complete_callback(context, messaging_result):
        context = 0
        print ( 'send_complete_callback called with context : {0}'.format(context) )
        print ( 'messagingResult : {0}'.format(messaging_result) )
    
  5. Add the following code to send a message to your device and handle the feedback message when the device acknowledges the cloud-to-device message:

    def iothub_messaging_sample_run():
        try:
            iothub_messaging = IoTHubMessaging(CONNECTION_STRING)
    
            iothub_messaging.open(open_complete_callback, OPEN_CONTEXT)
    
            for i in range(0, MESSAGE_COUNT):
                print ( 'Sending message: {0}'.format(i) )
                msg_txt_formatted = MSG_TXT % (AVG_WIND_SPEED + (random.random() * 4 + 2))
                message = IoTHubMessage(bytearray(msg_txt_formatted, 'utf8'))
    
                # optional: assign ids
                message.message_id = "message_%d" % i
                message.correlation_id = "correlation_%d" % i
                # optional: assign properties
                prop_map = message.properties()
                prop_text = "PropMsg_%d" % i
                prop_map.add("Property", prop_text)
    
                iothub_messaging.send_async(DEVICE_ID, message, send_complete_callback, i)
    
            try:
                # Try Python 2.xx first
                raw_input("Press Enter to continue...\n")
            except:
                pass
                # Use Python 3.xx in the case of exception
                input("Press Enter to continue...\n")
    
            iothub_messaging.close()
    
        except IoTHubError as iothub_error:
            print ( "Unexpected error {0}" % iothub_error )
            return
        except KeyboardInterrupt:
            print ( "IoTHubMessaging sample stopped" )
    
  6. Add the following main function:

    if __name__ == '__main__':
        print ( "Starting the IoT Hub Service Client Messaging Python sample..." )
        print ( "    Connection string = {0}".format(CONNECTION_STRING) )
        print ( "    Device ID         = {0}".format(DEVICE_ID) )
    
        iothub_messaging_sample_run()
    
  7. Save and close SendCloudToDeviceMessage.py file.

Run the applications

You are now ready to run the applications.

  1. Open a command prompt and install the Azure IoT Hub Device SDK for Python.

    pip install azure-iothub-device-client
    
  2. At the command prompt, run the following command to listen for cloud-to-device messages:

    python SimulatedDevice.py
    

    Run the simulated device app

  3. Open a new command prompt and install the Azure IoT Hub Service SDK for Python.

    pip install azure-iothub-service-client
    
  4. At a command prompt, run the following command to send a cloud-to-device message and wait for the message feedback:

    python SendCloudToDeviceMessage.py
    

    Run the app to send the cloud-to-device command

  5. Note the message received by the device.

    Message received

Next steps

In this tutorial, you learned how to send and receive cloud-to-device messages.

To see examples of complete end-to-end solutions that use IoT Hub, see Azure IoT Remote Monitoring solution accelerator.

To learn more about developing solutions with IoT Hub, see the IoT Hub developer guide.