Posted on 3 Comments

Device VS Module: A Somewhat Deep Dive into the Differences Between Azure IoT Hub Devices and Modules

What’s the real difference between an IoT Hub Module and an IoT Hub Device? What is an IoT Hub module and what is an IoT Hub device according to the development team? And why would I choose one over the other?

In this exploratory article I want to attempt to answer these questions for myself and other developers. This is one of those articles to help solidify my own understanding, as well as anyone else who comes across my blog. In other words, if you read something and think it needs correction, please, please, please correct me. If you want to discuss it, I’m open to that, as well. I’m always a student and always open to learning. And it’s an exploratory article, so don’t expect it to be succinct or completely organized. I’ll do my best to keep things clear, but I might jump around a bit. Basically, this is me thinking on paper and attempting to best organize my notes on these topics into something digestible to the reader.

Let’s dive in.

The first thing to understand is that there is a plethora of IoT Hub SDKs. They split into two primary categories:

  • Devices
  • Services

There are Azure IoT Hub Device SDKS, and there are Azure IoT Hub Service SDKs. I’m more interested in the device SDKs for this exploration, but it doesn’t hurt to touch on what the service SDKs offer.

The Azure IoT service SDKs contain code to facilitate building applications that interact directly with IoT Hub to manage devices and security.

Basically, if you need help managing the operations of IoT hub using software, these SDKs are the tool for you. That’s an interesting topic and I do want to eventually develop some tools in this area, but I won’t dive any deeper into the service SDK in this article.

That leaves us with the other branch, Azure IoT Hub Device SDKs:

The Microsoft Azure IoT device SDKs contain code that facilitates building applications that connect to and are managed by Azure IoT Hub services.

This is the area that I want to explore. Once I’ve drilled down this far, I still have a few choices. There are at the time of this article’s writing seven different device SDKs.

  • Azure IoT Hub device SDK for .Net
  • Azure IoT Hub device SDK for Embedded C (ANSI C – C99)
  • Azure IoT Hub device SDK for C (ANSI C – C99)
  • Azure IoT Hub device SDK for Java
  • Azure IoT Hub device SDK for Node.js
  • Azure IoT Hub device SDK for Python
  • Azure IoT Hub device SDK for iOS

That’s a lot of different language and framework choices. For this article, I’m going to focus on Python. Python, C, and C++ appear to be the languages of choice for the majority of IoT device developers. I think Python is the more approachable for developers like me who are more generalists than specialists, but I tell you what (Hank Hill voice) that SDK for Embedded C looks like a lot of fun. Working more with MCUs (Microcontroller Units) is definitely in my future, but right now I just want to be able to conquer the simple things, like an Arduino or Raspberry PI. I’ll stick with Python for now.

I guess that’s a use case clearly established. If you are looking to develop embedded device code, code that needs to run on very little memory on a very tiny device, you want to use the SDK for Embedded C.

This would probably be a great device to develop on for developers looking to create low-power IoT devices that use an MCU:

PIC-IoT WG Development Board

I’ll clarify further that this is not meant to be a step-by-step tutorial. I will include code snippets, I might even try a few things out on my little MXCHIP IoT DevKit, but I primarily want to focus on the use cases of devices VS modules.

Back on focus, let’s examine the actual Azure IoT Hub device SDK for Python. First, I’ll look at the API, and then I’ll dive into the actual source code.

The API

azure-iot-device package

The API is simple. It has one primary package, which is ‘device’. The device package has a couple of interesting things worth looking at. First, there are synchronous and asynchronous packages. So why are there two different versions? Why do we need both an async and a sync version of certain operations in the client SDK?

I think I intuitively know the answer to this question. Sync is probably there to support older versions of Python and maybe older technologies that will use the SDK. Async is generally preferred for communication in distributed systems because you don’t want to hold up operations because one of the parties in the communication isn’t responding. Let’s see how correct I am in my thinking.

Who can I ask about this? Well, I’m working in the SDK for an open-source application. Surely, the maintainer of the OSS has guideline for its developers. And it does! I’m turning to the Azure SDK developer’s guide with the hope it can shed some light.

.NET Azure SDK Design Guidelines

Ah, look! I searched through this document for the term, ‘Async’ and look what I found.

Sync and Async ✅ DO provide both asynchronous and synchronous variants for all service methods. Many developers want to port existing applications to the Cloud. These applications are often synchronous, and the cost of rewriting them to be asynchronous is usually prohibitive. Calling asynchronous APIs from synchronous methods can only be done through a technique called sync-over-async, which can cause deadlocks. The Azure SDK provides synchronous APIs to minimize friction when porting existing application to Azure. ✅ DO ensure that the names of the asynchronous and the synchronous variants differ only by the Async suffix

Additionally, the above quote provides a link to a nine year old article that’s still relevant.

Part 1 and 2 of the article on Sync VS Async. These are older articles, but they still hold up.

Should I expose asynchronous wrappers for synchronous methods? | .NET Parallel Programming

Should I expose synchronous wrappers for asynchronous methods? | .NET Parallel Programming

There’s a whole rabbit hole here that you can go down if you aren’t familiar with the reasons why you select sync or async in your development. I like the fact that the SDK developers are taking into consideration that some developers can’t easily work with async libraries and they’ve gone to the effort to provide both.

My takeaway, when you are developing greenfield projects and you won’t use a library that depends on synchronous operations, you should favor asynchronous over synchronous.

That narrows down what I want to really focus on in the API. I want to focus on the operations that are in the ‘aio’ package. Why is it called, ‘aio’? I’m certain that stands for, Asynchronous I/O. I’m confident enough that I don’t think I need to look it up, but someone can correct me if I’m wrong.

The aio package has both modules and classes. The module appears to be patch documentation. There’s only one function in this called:

execute_patch_for_async()

The documentation reads, “This module provides hard coded patches used to modify items from the libraries. Currently we have to do like this so that we don’t use exec anywhere.”

This appears to be some sort of SDK developer tool. Maybe when we start looking at the source, we’ll get a better idea what it is.

The classes are where the interesting stuff happens. We have 3 basic async classes:

  • IoTHubDeviceClient
  • IoTHubModuleClient
  • ProvisioningDeviceClient

Yay! We’re reached a couple of items that match our search. We have a device client and a module client. We also have a provisioning device client, but I think I’ll keep my focus on the first two.

IoTHubDeviceClient VS IoTHubModuleClient

To set focus, I’m trying to discern from the code and documentation related to IoT Hub Device Client and IoT Hub Module Client when one should be selected over the other. As I dive into the code and documentation, I’ll try to uncover these answers.

One thing I haven’t even asked myself, but I am now, is it even fair to expect to find these answers in the API or the code? I don’t know. That might be a bit too philosophical. Should the code clearly communicate its use case intent just by its very existence? We can look at DNA and determine that certain DNA is human while other DNA is a fly, but that might just be because we have the finished product of the DNA to compare. But DNA is more like the completed software designed with an SDK. I guess in our case the SDK is more like RNA. It’s the little factory pumping out DNA. So no, I might not find what I’m looking for by reviewing the SDK, but it’s still worth exploring just from a knowledge gathering standpoint (hey, I warned you that this was an exploratory article).

The IoTHubDeviceClient

This class has the following:

  • A constructor
  • Methods
    • connect
    • create_from_connection_string
    • create_from_sastoken
    • create_from_symmetric_key
    • create_from_x509_certificate
    • disconnect
    • notify_blob_upload_status
    • patch_twin_reported_properties
    • receive_message
    • receive_method_request
    • receive_twin_desired_properties_patch
    • send_message
    • send_method_response
    • update_sastoken
  • Attributes
    • on_message_received

This class has a lot going on, but all of it is related to a device communicating with IoT Hub.

Constructor

How do we build this class? The constructor code looks like this:

IoTHubDeviceClient(mqtt_pipeline, http_pipeline)

So what is an mqtt_pipeline and http_pipeline? There’s not much in the API documentation in the way of help on this one, so I’m going to go ahead and dive into the source to see what I can find.

Here’s our constructor, and there’s an interesting bit of instruction in there:

def __init__(self, **kwargs):

        “””Initializer for a generic synchronous client.

        This initializer should not be called directly.

        Instead, use one of the ‘create_from_’ classmethods to instantiate

        :param mqtt_pipeline: The MQTTPipeline used for the client

        :type mqtt_pipeline: :class:`azure.iot.device.iothub.pipeline.MQTTPipeline`

        :param http_pipeline: The HTTPPipeline used for the client

        :type http_pipeline: :class:`azure.iot.device.iothub.pipeline.HTTPPipeline`

        “””

        # Depending on the subclass calling this __init__, there could be different arguments,

        # and the super() call could call a different class, due to the different MROs

        # in the class hierarchies of different clients. Thus, args here must be passed along as

        # **kwargs.

        super(GenericIoTHubClient, self).__init__(**kwargs)

        self._inbox_manager = InboxManager(inbox_type=SyncClientInbox)

        self._handler_manager = sync_handler_manager.SyncHandlerManager(self._inbox_manager)

        # Set pipeline handlers

        self._mqtt_pipeline.on_connected = CallableWeakMethod(self, “_on_connected”)

        self._mqtt_pipeline.on_disconnected = CallableWeakMethod(self, “_on_disconnected”)

        self._mqtt_pipeline.on_method_request_received = CallableWeakMethod(

            self._inbox_manager, “route_method_request”

        )

        self._mqtt_pipeline.on_twin_patch_received = CallableWeakMethod(

            self._inbox_manager, “route_twin_patch”

        )

Ok, so clearly, we don’t need to worry about this constructor, because we shouldn’t ever try to create the class directly. Instead, we should always use one of the various connection methods to create the client. This is a good coding practice. The class is useless if it isn’t connected to IoT Hub.

My takeaway, if you are creating a device using the IoTHubDeviceClient expect to tightly couple it to an IoT Hub using one of the create_from_X methods. The device can’t live without a connection.

What about disconnecting? Is that handled for us automatically, too or do we need to manage that ourselves?

The API documentation does offer some direction here by stating, “It is recommended that you make sure to call this coroutine when you are completely done with your client instance.”

Yes, we should make the actual disconnection when we’re done with the client.

Let’s explore some of the other methods. I’ll start from the top and start working my way down, skipping over the various connection methods for now.

get_storage_info_for_blob, notify_blob_upload_status

get_storage_info_for_blob(blob_name)

From the documentation, “Sends a POST request over HTTP to an IoTHub endpoint that will return information for uploading via the Azure Storage Account linked to the IoTHub your device is connected to.”

The functionality of this method is covered here:

Upload files from devices to Azure IoT Hub with Python

Understand Azure IoT Hub file upload

If we have the correct tier of IoT Hub (Free or S1 and above) and an associated blob storage account, we can upload files from the device to the storage account by using the IoT Hub as a broker.

This is ideal for uploading large media files and telemetry batches. This is ideal for use cases that involve devices that are regularly disconnected from the internet or devices that capture large files. Think of a drone capturing soil data through high-definition imaging. Those high-definition images would need to transfer from the drone to a blob storage. And you might have smaller devices that send regular telemetry, but that device might log more data than you require for real time analysis. So that data could be uploaded in batches.

This is one interesting use case that doesn’t require IoT Edge. It can be done with something that is considered just a device.

For the completion of the upload process, we can use notify_blob_upload_status to, “When the upload is complete, the device sends a POST request to the IoT Hub endpoint with information on the status of an upload to blob attempt. This is used by IoT Hub to notify listening clients.”

notify_blob_upload_status(correlation_id, is_success, status_code, status_description)

patch_twin_reported_properties, receive_twin_desired_properties_patch, and get_twin

A device also has digital twin capabilities. I like what AWS calls this better than digital twin. They call it a device shadow, but either way it’s basically the same thing. A JSON representation of the device that is in both the device’s memory and the IoT Hub.

Device twins are JSON documents that store device state information including metadata, configurations, and conditions. Azure IoT Hub maintains a device twin for each device that you connect to IoT Hub.

There are three primary parts to a device twin document:

  • tags
  • desired properties
  • reported properties.

The use case for device twins:

  • Store device specific metadata in the cloud, like its location or another important device information
  • Report current device state.
  • Synchronize the state of long running workflows between the device app and back end apps.
  • Query your device metadata, configuration, or state.

Example of a device twin:

{

    “deviceId”: “devA”,

    “etag”: “AAAAAAAAAAc=”,

    “status”: “enabled”,

    “statusReason”: “provisioned”,

    “statusUpdateTime”: “0001-01-01T00:00:00”,

    “connectionState”: “connected”,

    “lastActivityTime”: “2015-02-30T16:24:48.789Z”,

    “cloudToDeviceMessageCount”: 0,

    “authenticationType”: “sas”,

    “x509Thumbprint”: {    

        “primaryThumbprint”: null,

        “secondaryThumbprint”: null

    },

    “version”: 2,

    “tags”: {

        “$etag”: “123”,

        “deploymentLocation”: {

            “building”: “43”,

            “floor”: “1”

        }

    },

    “properties”: {

        “desired”: {

            “telemetryConfig”: {

                “sendFrequency”: “5m”

            },

            “$metadata” : {…},

            “$version”: 1

        },

        “reported”: {

            “telemetryConfig”: {

                “sendFrequency”: “5m”,

                “status”: “success”

            },

            “batteryLevel”: 55,

            “$metadata” : {…},

            “$version”: 4

        }

    }

}

There’s a lot to the device twin capabilities. I won’t deep dive on it at this point, but the fact that devices support this capability means that devices are very powerful on their own, even without being edge modules. But what’s coming clear here is that we have a very basic client server relationship. The device is the client and, in some cases, the IoT Hub is the server or the gateway to a server.

With that in mind, let’s explore what we can do with the three functions made available to us.

get_twin()

This returns a complete twin as a JSON dictionary object. It can pull this from either the IoT Hub or an Edge Hub service. I’m not sure exactly what we would do with this data at this time, because I know that we can update device twin settings from the backend. If we needed to update the device settings from the backend, we could modify the desired properties of the device twin. So what might we need to do from the device itself? And what does get_twin() do for us?

According to the documentation, receive_twin_desired_properties_patch has been deprecated in favor of the on_twin_desired_properties_patch_received property. We also have patch_twin_reported_properties which will report properties with the Azure IoT Hub or Azure IoT Edge Hub service. If the service returns an error on the patch operation, this function will raise the appropriate error.

One other thing not mentioned in the SDKs that is important to understanding the “why,” behind device digital twins in the IoT Hub query language for device and module twins, jobs, and message routing.

As an aside, this is an import thing for a developer to learn and understand for working with the Azure IoT Hub suite of tools. It’s rather simple. If you are familiar with other query languages, like SQL or KQL you’ll find it easy to work with.

Taking all of this in, it’s clear that the digital twin methods, the backend query language, and the overall digital twin data object are all about monitoring and maintaining device health.

For instance, this little query is a good example of checking the status for multiple devices:

SELECT properties.reported.telemetryConfig.status AS status,

    COUNT() AS numberOfDevices

  FROM devices

  GROUP BY properties.reported.telemetryConfig.status

This can be done from backend systems in the same way that you would write in-line SQL.

Example in C#

var query = registryManager.CreateQuery(“SELECT * FROM devices”, 100);

while (query.HasMoreResults)

{

    var page = await query.GetNextAsTwinAsync();

    foreach (var twin in page)

    {

        // do work on twin object

    }

}

I’m seeing plenty of applications for device health, monitoring, and automation jobs. Again, very powerful capabilities that doesn’t seem to make devices a second-class citizen.

The potential of what could be done from the device side doesn’t seem too limited, either.

Messaging

Here we are. The biggest and probably most important part of an IoT device’s job. Messaging.

Sending and receiving messages.

send_message(message)

Sends a message to the default events endpoint on the Azure IoT Hub or Azure IoT Edge Hub instance. If the connection to the service has not previously been opened by a call to connect, this function will open the connection before sending the event.

The Message is a separate class that’s part of the azure.iot.device package. It won’t hurt to touch on it here, since it’s the thing we’re using as a primary means of transport. The Message object appears to be shared by both devices and modules, if that’s not the case I’ll probably know better when I dive into modules.

Like the Device class, the Message class also has a constructor, a set of variables, and a few methods. It makes sense to dive into those while we’re here to get a better understanding of what the Message class gives us.

The message constructor:

Message(data, message_id=None, content_encoding=None, content_type=None, output_name=None)

Unlike the device constructor, we clearly do want to set this ourselves in order to hydrate the class with the appropriate content needed to create a message.

Variables

data – data that constitutes the payload.

custom_properties – Dictionary of custom message properties. The keys and the values of these properties will always be string.

id – A user-settable identifier for the message sued for request-reply patterns. Format: A case-sensitive string (up to 128 characters long) of ASCII 7-bit alphanumeric characters + {‘-‘, ‘:’, ‘.’, ‘+’, ‘%’, ‘_’, ‘#’, ‘*’, ‘?’, ‘!’, ‘(‘, ‘)’, ‘,’, ‘=’, ‘@’, ‘;’, ‘$’, ”’}

expiry_time_utc – Date and time of message expiration in UTC format.

correlation_id – A property in a response message that typically contains the message_id of the request, in request-reply patterns.

user_id – An ID to specify the origin of the messages.

content_encoding – Content encoding of the message data. Can be ‘utf-8’, ‘utf-16’, or ‘utf-32’

content-type – Content type property used to route messages with the message-body. Can be ‘application/json’

output_name Name of the output that the message is being sent to.

input_name Name of the input that the message was received on.

There are only two methods.

get_size()

This gets the message size.

set_as_security_message()

This is listed as a provisional API. But it should set the message as a secure message.

So to return to our device analysis, the above is what is sent when we make the call to the send_message(message) method.

There are four specific exceptions that can be handled for the send_message(message) method.

  • CredentialError
  • ConnectionFailedError
  • ConnectionDroppedError
  • ClientError

These are self-explanatory.

receive_message()

This method is deprecated. It’s recommended to use the. on_message_received property to set a handler instead.

This is also the recommended means of responding to a method request. So we won’t cover that here.

send_method_response(method_response)

This method is used to respond to a method request from Azure IoT Hub or Azure IoT Edge Hub.

.on_message_received

This is the handler function or coroutine that will be called when a message is received. It should take an argument to receive a Message object.

The above represents a cloud-to-device message. Currently, the existing Python code samples I could find referenced the deprecated method. So how do we handle incoming messages? Basically, assign a method to the property that takes a message. That would look like the following.

# this is how I would likely create the handler

def message_received_handler(message):

        print(‘do something with message’ + message.data

# Here s where we set the handler

device_client.on_message_received = message_received_handler

This handler cuts down on the number of methods in the class.

This pretty much covers the device SDK. Next, I want to dive into the Module to see what options we have there. I’ll try not to duplicate areas that are shared by both. What I’m looking for are the areas of the module that really stand out for particular use cases where the device isn’t enough.

Modules

Now it’s time to closely examine the IoTHubModuleClient Class. Much like the device, it’s described as,

An asynchronous module client that connects to an Azure IoT Hub or Azure IoT Edge instance.

And much like the device, it has a constructor, methods, and attributes.

Constructor

One difference I noticed between the device and the module is a creation method titled, create_from_edge_environment. It reads that this allows the class to be created from the IoT Edge environment.

This method can only be run from inside an IoT Edge container, or in a debugging environment configured for Edge development (e.g., Visual Studio, Visual Studio Code)

What exactly is the Edge Environment?

Ok, so maybe we need to do a little time travelling here. Go back a bit. Let’s go back to the product that superseded IoT Edge. Let’s go back to the Azure IoT Gateway SDK. That’s right, because it had a 90s cool name like, Edge. It was known as Azure IoT Gateway SDK.

I’ve forked a version as close to the original as I could find, just for learning purposes.

shawndeggans/azure-iot-gateway-sdk

It’s clear to me from the name that IoT Edge wasn’t really meant to be a device itself, but a gateway for devices to indirectly communicate with the Azure IoT backend. The original documentation includes the following image.

In this original image the Gateway running on on-premises hardware is described as a module pipeline. Also notice that this original version appears to be a one-way trip. There’s device-to-cloud data, but not necessarily cloud-to-device messages.

It’s interesting looking at the early version of IoT Edge. Especially when you take into account what is needed to build a development environment. It appears much simpler. I don’t see Docker/Moby anywhere. We seem to be missing some of agents or at least there aren’t called out in the documentation. Overall, it looks to be a nice piece of middleware for working with devices. If its original intent was to be middleware, is it fair to say that it’s still middleware today? Or is it more like middleware that can also be a device?

Let’s continue to see if we can nail down what constitutes the IoT Edge environment. If we go by the origin project, the IoT Edge environment is a “module pipeline,” that accomplishes your specific scenario. Is that still the case?

What is Azure IoT Edge

Microsoft has this defined in the Azure Docs.

Azure IoT Edge moves cloud analytics and custom business logic to devices so that your organization can focus on business insights instead of data management. Scale out your IoT solution by packaging your business logic into standard containers, then you can deploy those containers to any of your devices and monitor it all from the cloud.

That does not sound like actual devices, but more things that would be on a server or in the cloud. Analytics is basically using data and math to answer business (domain specific) question. This is shaping up to sound a lot more like an environment or an operating system than a device.

Azure IoT Edge is made up of three primary components:

  • IoT Edge Modules
  • IoT Edge Runtime
  • A Cloud-based Interface

IoT Modules are containers. Basically, these are Docker files that contain Azure services, third-party services, or our own custom applications. This brings us back to the actual SDK, which doesn’t mention anything about Docker (or Moby as the case is for the IoT Edge Environment), but judging by some of the methods (which I will dive into later), they are not necessarily meant to be the terminus of the solution, but they should support the responsibility of middleware.

What about the IoT Edge Runtime? Does it still hold up with the earlier definition of a “pipeline of modules”?

IoT Edge Runtime

The Azure IoT Edge runtime enables custom and cloud logic on IoT Edge devices. The runtime sits on the IoT Edge device and performs management and communication operations.

Hmm, this does sound a bit like an operating system or an orchestration piece. Here are some of its responsibilities:

  • Installs and updates workloads on the device.
  • Maintains Azure IoT Edge security standards on the device.
  • Ensures that IoT Edge modules are always running.
  • Reports module health to the cloud for remote monitoring.
  • Manages communication between downstream leaf devices (what I want to call Actual Devices) and an IoT Edge device (or what I would rather call a Gateway or middleware), between modules on an IoT Edge device, and between an IoT Edge device and the cloud.

I think this might have been an early point of confusion for me. The documentation continues to call IoT Edge modules running within the IoT Edge runtime a “device”. I realize it can be a device, but it’s so much more. To me, this is more a server than a client.

Here’s an image from the documentation that shows what look like robot arms (devices) sending telemetry to the Azure IoT Edge runtime, and later to the cloud using IoT Hub as a gateway.

IoT Edge cloud

This basically refers to the backend systems and the overall operation of managing a fleet of IoT devices. Azure offers several possible solutions for this backend work with IoT Hub being the queen bee of the hive and IoT Central being a very close second.

Back to IoTHubModuleClient Class

Ok, that was a bit of a rabbit hole, but I think it was necessary to put things in perspective. Microsoft approaches the IoT Edge devices as actual devices, because they clearly can be, but they can also be hubs, gateways, storage, queues, etc.. This flexibility can make it difficult for someone learning all of this to implement the correct solution when deciding how to approach solution engineering IoT architectures.

The module has many of the same connection abilities as the device, with the exception of the ability to connect within an environment.

There are also different types of methods within the module that point to this duality. Let’s see if we can pick those apart and determine if they are device-like methods, middleware-like methods, or both?

As far as the Digital Twins methods are concerned, they appear to be identical to the device Digital Twin methods. It even has the same deprecation status on the message receiving end. So I think we can just consider these universal.

invoke_method is probably the first one that looks like middleware.

Invoke a method from your client onto a device or module client, and receive the response to the method call.

invoke_method(method_params, device_id, module_id=None)

Clearly this is designed for one module to be able to call a method on either a device or another module. It’s directly addressed, so I assume this is handled by the runtime.

send_message_to_output(message, output_name)

Sends an event/message to the given module output. These are outgoing events and are meant to be “output events” If the connection to the service has not previously been opened by a call to connect, this function will open the connection before sending the event.

This is another method that only exists in the IoT Hub Module Client. The output that this refers to is the output channel on IoT Hub.

update_sastoken(sastoken)

Update the client’s SAS Token used for authentication, then reauthorizes the connection. This API can only be used if the client was initially created with a SAS Token. Note also that this API may return before the reauthorization/reconnection is completed. This means that some errors that may occur as part of the reconnection could occur in the background and will not be raised by this method.

This is only on the IoT Edge module and allows a call to the device to update the SAS Token, if it was created with a SAS Token. This is useful for managing security key rotation.

My closing opinion

Despite much of my earlier confusion around devices and modules, I think it’s very clear to me now when one should be selected over the other.

Devices

Use the Azure IoT Hub Device SDK when you want to create a device that will primarily serve as a data collection tool. This can actually be a fairly sophisticated piece of code but remember that it has a singular purpose. For instance, right now I’m creating a device for a Raspberry Pi that is collecting moisture for a plant. That’s not an IoT Edge Module. It doesn’t need to be. It just needs to run on the Pi and send telemetry to Azure IoT Hub. It doesn’t need AI, it doesn’t need event processing, no business logic, and no interaction with other devices other than IoT Hub. In my opinion, this is the ideal use case for a device and not a module.

Modules

When I want to bring some functionality of the cloud down to a device to make it faster, easier to work with in a disconnected state, or to work with AI, I will use the Azure IoT Hub Module SDK to create an Azure IoT Edge device. I still don’t like calling it a device but keeping these basic ideas in mind will help build clarity around my future architectures. I do have some plans to put IoT Edge into service using a model built from a ML project. And I want to use Azure Blob Storage on the Edge, so there’s a good use case.

Overall, I think the Azure IoT platform is wonderful. I love working with it. I always feel like I’m just scratching the surface of its capabilities, but there’s a lot to learn and some of it can be confusing for the IoT beginner. I hope my long winding exploration has been helpful. Expect future explorations like this. I know it’s a little raw, but it does allow me to get my complete thoughts down and come to working conclusions.


If you enjoyed this post, you may also want to read my recent exploration of the various Azure IoT SDKs.

3 thoughts on “Device VS Module: A Somewhat Deep Dive into the Differences Between Azure IoT Hub Devices and Modules

  1. […] Device VS Module: A Somewhat Deep Dive into the Differences Between Azure IoT Hub Devices and M… How do you use AI to improve reliability? […]

  2. WELL-AUTHORED article – NICELY DONE !

    SO MANY THINGS covered SO WELL, and from an appropriate you’re-not-a-newbie-but-there’s-a-LOT-to-unpack-here-and-even-MORE-to-learn-later perspective.

    I really appreciate you taking the time (likely DAYS) to prepare this article.

    Even though I was seeking help with the IoTHubModuleClient.create_from_connection_string() function, which never got treated, I still ended up with SO MUCH of my own current understanding “validated” by another professional that I’m STILL glad I came across your article !

    I would only humbly provide this small typo correction:
    “it’s” is ONLY meant to replace “it is” – it’s is a CONTRACTION, NOT an indication of POSSESSION.

    When its is used to describe POSSESSING something, you simply use “its” WITHOUT the apostrophe.

    The WRONG RULE to use is this:
    Mark’s
    Shawn’s
    Laurie’s
    It’s <— this is the WRONG use

    The RIGHT RULE (quite interesting) is this:
    His <– denotes GENDER
    Hers <– denotes GENDER
    Its <– denotes GENDERLESS possession, notice NONE of these have apostrophes!

    Your new ally,
    -Mark Vogt,

    1. Thanks, Mark! I’m glad you found the article valuable. And thanks for spotting the typo. I’ll correct it soon. Is there any question I can answer related to the connection string?

Leave a Reply