Tutorial: Develop and deploy a Python IoT Edge module to your simulated device

You can use Azure IoT Edge modules to deploy code that implements your business logic directly to your IoT Edge devices. This tutorial walks you through creating and deploying an IoT Edge module that filters sensor data. You'll use the simulated IoT Edge device that you created in the quickstarts. In this tutorial, you learn how to:

  • Use Visual Studio Code to create an IoT Edge Python module.
  • Use Visual Studio Code and Docker to create a Docker image and publish it to your registry.
  • Deploy the module to your IoT Edge device.
  • View generated data.

The IoT Edge module that you create in this tutorial filters the temperature data that's generated by your device. It only sends messages upstream if the temperature is above a specified threshold. This type of analysis at the edge is useful for reducing the amount of data that's communicated to and stored in the cloud.

If you don't have an Azure subscription, create a free account before you begin.

Prerequisites

An Azure IoT Edge device:

  • You can use your development machine or a virtual machine as an Edge device by following the steps in the quickstart for Linux.
  • Python modules for IoT Edge don't support Windows devices.

Cloud resources:

  • A free or standard-tier IoT Hub in Azure.

Development resources:

Note

Ensure that your bin folder is on your path for your platform. Typically ~/.local/ for UNIX and macOS, or %APPDATA%\Python on Windows.

Create a container registry

In this tutorial, you use the Azure IoT Edge extension for VS Code to build a module and create a container image from the files. Then you push this image to a registry that stores and manages your images. Finally, you deploy your image from your registry to run on your IoT Edge device.

You can use any Docker-compatible registry for this tutorial. Two popular Docker registry services that are available in the cloud are Azure Container Registry and Docker Hub. This tutorial uses Azure Container Registry.

  1. In the Azure portal, select Create a resource > Containers > Azure Container Registry.
  2. Give your registry a name, choose a subscription, choose a resource group, and set the SKU to Basic.
  3. Select Create.
  4. After your container registry is created, browse to it, and select Access keys.
  5. Toggle Admin user to Enable.
  6. Copy the values for Login server, Username, and Password. You use these values later in the tutorial.

Create an IoT Edge module project

The following steps create an IoT Edge Python module by using Visual Studio Code and the Azure IoT Edge extension.

Create a new solution

Use the Python package cookiecutter to create a Python solution template that you can build on top of.

  1. In Visual Studio Code, select View > Integrated Terminal to open the VS Code integrated terminal.

  2. In the integrated terminal, enter the following command to install (or update) cookiecutter, which you use to create the IoT Edge solution template in VS Code:

    pip install --upgrade --user cookiecutter
    

    Note

    Ensure the directory where cookiecutter will be installed is in your environment’s Path in order to make it possible to invoke it from a command prompt. Typically, on Windows, add %APPDATA%\Python\PythonVersion\Scripts, where you have your version of Python instead of PythonVersion.

  3. Select View > Command Palette to open the VS Code command palette.

  4. In the command palette, enter and run the command Azure: Sign in and follow the instructions to sign in your Azure account. If you're already signed in, you can skip this step.

  5. In the command palette, enter and run the command Azure IoT Edge: New IoT Edge solution. In the command palette, provide the following information to create your solution:

    1. Select the folder where you want to create the solution.
    2. Provide a name for your solution or accept the default EdgeSolution.
    3. Choose Python Module as the module template.
    4. Name your module PythonModule.
    5. Specify the Azure container registry that you created in the previous section as the image repository for your first module. Replace localhost:5000 with the login server value that you copied. The final string looks like <registry name>.azurecr.io/pythonmodule.

    Provide Docker image repository

The VS Code window loads your IoT Edge solution workspace. The solution workspace contains five top-level components. You won't edit the .gitignore file in this tutorial. The modules folder contains the Python code for your module as well as Dockerfiles for building your module as a container image. The .env file stores your container registry credentials. The deployment.template.json file contains the information that the IoT Edge runtime uses to deploy modules on a device.

If you didn't specify a container registry when creating your solution, but accepted the default localhost:5000 value, you won't have a .env file.

Python solution workspace

Add your registry credentials

The environment file stores the credentials for your container repository and shares them with the IoT Edge runtime. The runtime needs these credentials to pull your private images onto the IoT Edge device.

  1. In the VS Code explorer, open the .env file.
  2. Update the fields with the username and password values that you copied from your Azure container registry.
  3. Save this file.

Update the module with custom code

Each template includes sample code, which takes simulated sensor data from the tempSensor module and routes it to the IoT hub. In this section, add the code that expands the PythonModule to analyze the messages before sending them.

  1. In the VS Code explorer, open modules > PythonModule > main.py.

  2. At the top of the main.py file, import the json library:

    import json
    
  3. Add the TEMPERATURE_THRESHOLD and TWIN_CALLBACKS variables under the global counters. The temperature threshold sets the value that the measured machine temperature must exceed for the data to be sent to the IoT hub.

    TEMPERATURE_THRESHOLD = 25
    TWIN_CALLBACKS = 0
    
  4. Replace the receive_message_callback function with the following code:

    # receive_message_callback is invoked when an incoming message arrives on the specified 
    # input queue (in the case of this sample, "input1").  Because this is a filter module, 
    # we forward this message to the "output1" queue.
    def receive_message_callback(message, hubManager):
        global RECEIVE_CALLBACKS
        global TEMPERATURE_THRESHOLD
        message_buffer = message.get_bytearray()
        size = len(message_buffer)
        message_text = message_buffer[:size].decode('utf-8')
        print ( "    Data: <<<%s>>> & Size=%d" % (message_text, size) )
        map_properties = message.properties()
        key_value_pair = map_properties.get_internals()
        print ( "    Properties: %s" % key_value_pair )
        RECEIVE_CALLBACKS += 1
        print ( "    Total calls received: %d" % RECEIVE_CALLBACKS )
        data = json.loads(message_text)
        if "machine" in data and "temperature" in data["machine"] and data["machine"]["temperature"] > TEMPERATURE_THRESHOLD:
            map_properties.add("MessageType", "Alert")
            print("Machine temperature %s exceeds threshold %s" % (data["machine"]["temperature"], TEMPERATURE_THRESHOLD))
        hubManager.forward_event_to_output("output1", message, 0)
        return IoTHubMessageDispositionResult.ACCEPTED
    
  5. Add a new function called module_twin_callback. This function is invoked when the desired properties are updated.

    # module_twin_callback is invoked when the module twin's desired properties are updated.
    def module_twin_callback(update_state, payload, user_context):
        global TWIN_CALLBACKS
        global TEMPERATURE_THRESHOLD
        print ( "\nTwin callback called with:\nupdateStatus = %s\npayload = %s\ncontext = %s" % (update_state, payload, user_context) )
        data = json.loads(payload)
        if "desired" in data and "TemperatureThreshold" in data["desired"]:
            TEMPERATURE_THRESHOLD = data["desired"]["TemperatureThreshold"]
        if "TemperatureThreshold" in data:
            TEMPERATURE_THRESHOLD = data["TemperatureThreshold"]
        TWIN_CALLBACKS += 1
        print ( "Total calls confirmed: %d\n" % TWIN_CALLBACKS )
    
  6. In the HubManager class, add a new line to the init method to initialize the module_twin_callback function that you just added:

    # Sets the callback when a module twin's desired properties are updated.
    self.client.set_module_twin_callback(module_twin_callback, self)
    
  7. Save this file.

Build your IoT Edge solution

In the previous section, you created an IoT Edge solution and added code to the PythonModule to filter out messages where the reported machine temperature is below the acceptable threshold. Now you need to build the solution as a container image and push it to your container registry.

  1. Sign in to Docker by entering the following command in the Visual Studio Code integrated terminal. Then you can push your module image to your Azure container registry:

    docker login -u <ACR username> -p <ACR password> <ACR login server>
    

    Use the username, password, and login server that you copied from your Azure container registry in the first section. You can also retrieve these values from the Access keys section of your registry in the Azure portal.

  2. In the VS Code explorer, open the deployment.template.json file in your IoT Edge solution workspace.

    This file tells the $edgeAgent to deploy two modules: tempSensor, which simulates device data, and PythonModule. The PythonModule.image value is set to a Linux amd64 version of the image. To learn more about deployment manifests, see Understand how IoT Edge modules can be used, configured, and reused.

    This file also contains your registry credentials. In the template file, your user name and password are filled in with placeholders. When you generate the deployment manifest, the fields are updated with the values that you added to the .env file.

  3. Add the PythonModule module twin to the deployment manifest. Insert the following JSON content at the bottom of the moduleContent section, after the $edgeHub module twin:

        "PythonModule": {
            "properties.desired":{
                "TemperatureThreshold":25
            }
        }
    
  4. Save this file.

  5. In the VS Code explorer, right-click the deployment.template.json file and select Build and Push IoT Edge solution.

When you tell Visual Studio Code to build your solution, it first takes the information in the deployment template and generates a deployment.json file in a new folder named config. Then it runs two commands in the integrated terminal: docker build and docker push. These two commands build your code, containerize the Python code, and then push the code to the container registry that you specified when you initialized the solution.

You can see the full container image address with tag in the docker build command that runs in the VS Code integrated terminal. The image address is built from information in the module.json file with the format <repository>:<version>-<platform>. For this tutorial, it should look like registryname.azurecr.io/pythonmodule:0.0.1-amd64.

Deploy and run the solution

In the quickstart article that you used to set up your IoT Edge device, you deployed a module by using the Azure portal. You can also deploy modules using the Azure IoT Toolkit extension for Visual Studio Code. You already have a deployment manifest prepared for your scenario, the deployment.json file. All you need to do now is select a device to receive the deployment.

  1. In the VS Code command palette, run Azure IoT Hub: Select IoT Hub.

  2. Choose the subscription and IoT hub that contain the IoT Edge device that you want to configure.

  3. In the VS Code explorer, expand the Azure IoT Hub Devices section.

  4. Right-click the name of your IoT Edge device, then select Create Deployment for Single Device.

    Create deployment for single device

  5. Select the deployment.json file in the config folder and then click Select Edge Deployment Manifest. Do not use the deployment.template.json file.

  6. Click the refresh button. You should see the new PythonModule running along with the TempSensor module and the $edgeAgent and $edgeHub.

View generated data

Once you apply the deployment manifest to your IoT Edge device, the IoT Edge runtime on the device collects the new deployment information and starts executing on it. Any modules running on the device that aren't included in the deployment manifest are stopped. Any modules missing from the device are started.

You can view the status of your IoT Edge device using the Azure IoT Hub Devices section of the Visual Studio Code explorer. Expand the details of your device to see a list of deployed and running modules.

On the IoT Edge device itself you can see the status of your deployment modules using the command iotedge list. You should see four modules: the two IoT Edge runtime modules, tempSensor, and the custom module that you created in this tutorial. It may take a few minutes for all the modules to start, so rerun the command if you don't see them all initially.

To view the messages being generated by any module, use the command iotedge logs <module name>.

You can view the messages as they arrive at your IoT hub using Visual Studio Code.

  1. To monitor data that arrives at the IoT hub, select the ellipsis (...), and then select Start Monitoring D2C Messages.
  2. To monitor the D2C message for a specific device, right-click the device in the list, and select Start Monitoring D2C Messages.
  3. To stop monitoring data, run the command Azure IoT Hub: Stop monitoring D2C message in the command palette.
  4. To view or update the module twin, right-click the module in the list, and select Edit module twin. To update the module twin, save the twin JSON file, right-click the editor area, and select Update Module Twin.
  5. To view Docker logs, install Docker for VS Code. You can find your running modules locally in Docker explorer. In the context menu, click Show Logs to view in the integrated terminal.

Clean up resources

If you plan to continue to the next recommended article, you can keep the resources and configurations that you created and reuse them. You can also keep using the same IoT Edge device as a test device.

Otherwise, you can delete the local configurations and the Azure resources that you created in this article to avoid charges.

Delete Azure resources

Deleting Azure resources and resource groups is irreversible. Make sure that you don't accidentally delete the wrong resource group or resources. If you created the IoT hub inside an existing resource group that has resources that you want to keep, delete only the IoT hub resource itself, instead of deleting the resource group.

To delete the resources:

  1. Sign in to the Azure portal and select Resource groups.

  2. Select the name of the resource group that contains your IoT Edge test resources.

  3. Review the list of resources contained in your resource group. If you want to delete all of them, you can select Delete resource group. If you want to delete only some of them, you can click into each resource to delete them individually.

Delete local resources

If you want to remove the IoT Edge runtime and related resources from your device, use the following commands.

Remove the IoT Edge runtime.

sudo apt-get remove --purge iotedge

When the IoT Edge runtime is removed, the containers that it created are stopped, but still exist on your device. View all containers.

sudo docker ps -a

Delete the runtime containers that were created on your device.

docker rm -f edgeHub
docker rm -f edgeAgent

Delete any additional containers that were listed in the docker ps output by referring to the container names.

Remove the container runtime.

sudo apt-get remove --purge moby

Next steps

In this tutorial, you created an IoT Edge module with code to filter raw data that's generated by your IoT Edge device. You can continue on to the next tutorials to learn other ways that Azure IoT Edge can help you turn data into business insights at the edge.