Introduction
One of the really nice things about developing your secure IoT product with Azure Sphere is that Microsoft has implemented all the hooks and OS Services for your Azure Sphere application to leverage Azure IoT Hub features for Cloud to Device (C2D) communication/control. Three common D2C tools available from Azure IoT Hubs are Device Twins, Remote Messages, and Direct Method calls. This blog will introduce the reader to Direct Method calls.
What is a Direct Method?
From the Microsoft documentation: " . . . Direct methods represent a request-reply interaction with a device similar to an HTTP call in that they succeed or fail immediately (after a user-specified timeout). This approach is useful for scenarios where the course of immediate action is different depending on whether the device was able to respond."
You can think of a Direct Method as a method implemented in your Azure Sphere application that can be invoked from the Cloud. Direct Methods accept a JSON object argument to pass any arguments that your Direct Method needs to accomplish its purpose in life. Additionally, your Direct Method can return a JSON object. The JSON argument and response objects can be up to 128 KB in size, so you have lots of flexibility in both directions.
When should you use a Direct Method?
When deciding if you should use a Direct Method, you should ask yourself one question. Does my device have to be on-line and connected to my Azure IoT Hub when I invoke the Direct Method? If your answer is yes, then you should consider using a Direct Method.
Examples of when you may want to use a Direct Method:
- I have an IoT device that should respond to a user request in almost real-time
- Connected garage door opener: Open Door, Close Door
- Connected light bulb: Turn on, Turn off
- Connected Car: Unlock door
Examples where you may not want to use a Direct Method:
In these cases you should consider the scenario where the device may not be connected to your Azure IoT Hub, but you want to push a change down to the device; you don't care when the change takes place as long as it happens eventually. Using a Device Twin desired property would be a better choice.
- I have a device that may not be connected and I don't need the change to happen in near real-time
- I want to change the telemetry reporting cadence
- I want to change a static message on a display
- I want to enable a new feature on a device
Direct Method Specifics
There are a few things you should know about using Direct Methods
- Support for Direct Methods is only available on Azure IoT Hubs using the standard tier pricing or higher
- Direct Method calls from Azure target a single device
- Direct Methods follow a request-response pattern
- Direct Method argument/response JSON objects can be up to 128 KB in size
- If a Direct Method call fails, the call will not retry, that is, it will NOT be sent when the device reconnects to the IoT Hub
How to implement a Direct Method
I've seen some example applications where Direct Methods were used, but I wanted to get a better grasp on how to use them. I decided to add two Direct Methods to the Avnet Azure Sphere Out-of-Box application. This would require me to learn about the feature, give me some hands on experience, and document how to implement them for this blog.
What are the things I need to do to implement a Direct Method?
Implementing the Direct Method is a simple process. Below I've listed the minimum things your application needs to do/implement to enable Direct Methods in your application.
- Define your new Direct Method
- Define the name of your Direct Method
- Define the argument JSON Object and data items inside the object
- Define the response JSON Object to return to Azure
- Inform the OS Services that I have a callback method for any Direct Method messages from the Azure IoT Hub
- Implement the callback method
- Identify which Direct Method was called
- Parse the JSON argument
- Do something meaningful for the Direct Method Call
- Construct and send a JSON object response back to my IoT Hub
My Direct Method Implementation
I decided to implement two different Direct Methods.
(1) Define your new Direct Method
Below are the high level details for my two Direct Methods:
Direct Method name: haltApplication
Direct Method Purpose: This Direct Method will cause the application to exit by setting the global terminationRequired flag to true
Direct Method Arguments: This Direct Method does not require any arguments. Note that the user can pass any arguments into this Direct Method, however all arguments are ignored.
Direct Method Response: The Direct Method has one response:
- { "success" : true, "message" : "Halting Application" }
Direct Method name: setSensorPollTime
Direct Method Purpose: This Direct Method will dynamically change the time period between polling the on-board ST-Micro sensors. Note the default period is 1 second. The Direct Method change will not be persistent. That is, if the application is restarted the default poll time of 1 second will be used. This function could be improved by writing the new setting to persistent memory and reading the persistent memory on startup.
Direct Method Arguments: This Direct Method requires a JSON object { "pollTime" : <time in seconds greater than 0> }
Direct Method Responses: The Direct Method implements two different responses. One for success and one when the argument does not meet the specifications.
- { "success" : true, "message" : "New Sensor Poll Time <new poll time> seconds" }
- { "success" : false, "message" : "request does not contain an identifiable payload" }
(2) Inform the OS Services that I have a callback method for any Direct Method messages from IoT Hub
We use the void AzureIoT_SetDirectMethodCallback(DirectMethodCallFnType callback) routine to inform the OS services about our callback function. In my application I call this method in the InitPeripheralsAndHandlers(void) routine that gets called when the application starts up.
(3) Implement the callback method
The callback method must use a specific function signature shown below:
static int <Your_Callback_Method_Name> (const char *methodName, const char *payload, size_t payloadSize, char **responsePayload, size_t *responsePayloadSize)
Annotated Code Example
The graphic below calls out each of the pieces needed to implement the Direct Method. You can click on the graphic to see a larger version of the picture.
How to invoke a Direct Method
My experience with Direct Methods has been to open a Direct Method interface and type in the Direct Method name and the JSON object specifying the arguments. I'm sure there are ways the Direct Methods can be called programmatically from Azure or from applications that have access to the IoT Hub. I'll leave that exercise for a later date. For now, let me show you how to call each of my Direct Methods using the manual method.
There are at least three different interfaces I've used to send Direct Method calls. Using the Azure Portal, Visual Studio Cloud Explorer, and using the Device Explorer utility. I'll review how to use the Azure Portal method since it's a clean interface and if you have access to your IoT Hub and your specific IoT device you're good to go.
Using the Azure Portal to call Direct Methods
- Open the Azure Portal and sign in
- Find your IoT Hub, open the IoT Hub resource
- From the IoT Hub instance find the "IoT devices" blade, then your IoT device
- Once you're inside the page for your IoT Device look along the top of the window. Here you'll see some of the things you can do with your IoT device. Note that the three ways we identified to send C2D message are all represented in this interface. Since this blog is all about Direct Methods, click on the Direct Method link.
- This will open the Direct Method interface. Below I'll describe each item in this interface:
- Invoke Method: This is the GO button. After you enter the Method Name and any Payload details, click on the "Invoke Method" link to send the Direct Method call to your device.
- Method Name: This is where, you guessed it, you type in the name of the Direct Method you want to call in your application. Note that this name is case sensitive.
- Payload: This is where you type in the JSON structure with the arguments for your Direct Method.
- Result: This is where the JSON return object will be displayed.
Let's see it in action:
In the graphic below I'm calling the setSensorPollTime Direct Method and I'm setting the new poll time to 5 seconds.
Next I click on the "Invoke Method" link:
- Azure IoT Hub sends the Direct Method Call to my device
- The Azure Sphere OS Services catch the message and invoke my directMethodCall() routine and include all the required arguments
- The routine determines that the setSensorPollTime Direct Method was called
- The setSensorPollTime code parses the argument JSON object and validates that the data is good
- The setSensorPollTime code changes the poll timer
- The setSensorPollTime code constructs the return JSON object and sends it back to the Azure IoT Hub
You can see that the return JSON object is displayed in the Azure Portal inside the Result window! Like magic!
Hands on Exercise
Now lets load some code onto our Avnet Azure Sphere Starter Kit and try out the new Direct Methods. The prerequisites for this exercise are to complete the first two Out of Box Blogs from the links below. Once you have loaded the OOB example application and connected it to your IoT Hub, come back to this blog for further instructions.
Note: If you've previously worked through the OOB blogs (before 9/12/19), you'll need to pull the latest code from GitHub.
- Complete the OOB blog 1 of 3 (Pull and load the GitHub project)
- Complete the OOB blog 2 of 3 (Create an IoT Hub, IoT device and connect your application to the IoT Hub)
- Make sure the example application is running on your Starter Kit
- Make sure the example application is connected to the Azure IoT Hub
- Open the Azure Portal and login to your Azure account
- Find your IoT Hub
- Find the "IoT devices" blade
- Find your IoT device
- Open the "Direct Method" interface
- Enter the following information:
- Method Name: setSensorPollTime
- Payload: { "pollTime" : 10 }
- Watch the debug output in Visual Studio. Note that the sensors are read every 1 second
- Back in the Azure Portal click on the "Invoke Method" link
- Watch the debug output in Visual Studio. You should see debug output stating that the poll time was changed and you should see the time between sensor reads go from 1 second to 10 seconds.
- Review the result window in the Azure Portal and note the return JSON object
- Now go back to the Azure Portal Direct Method interface; lets call the second Direct Method
- Method Name: haltApplication
- Payload: { }
- Click on the "Invoke Method" link
- Go back to your Visual Studio application and note that the application has halted
- Review the result window in the Azure Portal, note the return JSON object
Conclusion
Direct Methods allow users to invoke code on a remote connected device. Because you can pass large JSON arguments to the Direct Method, your imagination is the only limitation to how you could use this powerful Cloud-to-Device mechanism.
If you have used Direct Methods to implement features or functionalities in your own IoT deployment, please add a comment with a high level overview of what you did. This will help other developers generate ideas.
Thanks for reading,
Brian
Top Comments