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

Introduction

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 Get started with IoT Hub tutorial 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 only available in the standard tier of IoT hub. For more information about the basic and standard IoT Hub tiers, see How to choose the right IoT Hub tier.

This tutorial builds on Get started with 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 acknowledgement (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 Get started with 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 acknowledgement.

Note

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

To complete this tutorial, you need the following:

Note

The pip packages for azure-iothub-service-client and azure-iothub-device-client are currently available only for Windows OS. For Linux/Mac OS, please refer to the Linux and Mac OS-specific sections on the [Prepare your development environment for Python][lnk-python-devbox] post.

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 time
    import sys
    import iothub_client
    from iothub_client import IoTHubClient, IoTHubClientError, IoTHubTransportProvider, IoTHubClientResult
    from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError
    
    RECEIVE_CONTEXT = 0
    WAIT_COUNT = 10
    RECEIVED_COUNT = 0
    RECEIVE_CALLBACKS = 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 Get started with IoT Hub tutorial:

    # choose AMQP or AMQP_WS as transport protocol
    PROTOCOL = IoTHubTransportProvider.AMQP
    CONNECTION_STRING = "{deviceConnectionString}"
    
  4. Add the following function to print received messages to the console:

    def receive_message_callback(message, counter):
        global RECEIVE_CALLBACKS
        message_buffer = message.get_bytearray()
        size = len(message_buffer)
        print ( "Received Message [%d]:" % counter )
        print ( "    Data: <<<%s>>> & Size=%d" % (message_buffer[:size].decode('utf-8'), size) )
        map_properties = message.properties()
        key_value_pair = map_properties.get_internals()
        print ( "    Properties: %s" % key_value_pair )
        counter += 1
        RECEIVE_CALLBACKS += 1
        print ( "    Total calls received: %d" % RECEIVE_CALLBACKS )
        return IoTHubMessageDispositionResult.ACCEPTED
    
    def iothub_client_init():
        client = IoTHubClient(CONNECTION_STRING, PROTOCOL)
    
        client.set_message_callback(receive_message_callback, RECEIVE_CONTEXT)
    
        return client
    
    def print_last_message_time(client):
        try:
            last_message = client.get_last_message_receive_time()
            print ( "Last Message: %s" % time.asctime(time.localtime(last_message)) )
            print ( "Actual time : %s" % time.asctime() )
        except IoTHubClientError as iothub_client_error:
            if iothub_client_error.args[0].result == IoTHubClientResult.INDEFINITE_TIME:
                print ( "No message received" )
            else:
                print ( iothub_client_error )
    
  5. Add the following code to initialize the client and wait to receive the cloud-to-device message:

    def iothub_client_init():
        client = IoTHubClient(CONNECTION_STRING, PROTOCOL)
    
        client.set_message_callback(receive_message_callback, RECEIVE_CONTEXT)
    
        return client
    
    def iothub_client_sample_run():
        try:
            client = iothub_client_init()
    
            while True:
                print ( "IoTHubClient waiting for commands, press Ctrl-C to exit" )
    
                status_counter = 0
                while status_counter <= WAIT_COUNT:
                    status = client.get_send_status()
                    print ( "Send status: %s" % status )
                    time.sleep(10)
                    status_counter += 1
    
        except IoTHubError as iothub_error:
            print ( "Unexpected error %s from IoTHub" % iothub_error )
            return
        except KeyboardInterrupt:
            print ( "IoTHubClient sample stopped" )
    
        print_last_message_time(client)
    
  6. Add the following main function:

    if __name__ == '__main__':
        print ( "Starting the IoT Hub Python sample..." )
        print ( "    Protocol %s" % PROTOCOL )
        print ( "    Connection string=%s" % CONNECTION_STRING )
    
        iothub_client_sample_run()
    
  7. Save and close SimulatedDevice.py file.

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 Get started with IoT Hub tutorial. You also need the IoT Hub connection string for your hub that you can find in the Azure portal.

  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 "{IoTHubConnectionString}" placeholder value with the IoT Hub connection string for the hub you created in the Get started with IoT Hub tutorial. Replace the "{deviceId}" placeholder with the device ID of the device you added in the Get started with IoT Hub tutorial:

    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.