创建一个Azure IoT Edge Python模块,以从边缘设备收集数据并将其传输到IoT Hub
#python #edge #azureiot #runtime

介绍

Azure IoT运行时,您可以远程收集信息并在边缘设备上执行命令。安装在E-RT3 Plus上时,可以利用E-RT3 Plus和Azure运行时环境的功能执行各种操作。

这是五部分系列的第二部分,该系列演示了如何使用E-RT3 Plus使用Azure运行时环境。在previous article中,我们创建了一个Python模块,该模块将数据从模拟温度传感器模块发送到IoT Hub。在本文中,我们创建了一个python iot边缘数据收集模块,该模块从模拟输入模块(F3AD08-6R)收集数据并将其上传到IoT Hub。

本文系列的最终目标是演示如何:

  1. 创建一个Python数据收集模块来收集数据,处理并将其上传到IoT Hub
  2. 创建一个用于将数据写入边缘设备
  3. 的Python模块
  4. 使用Azure App服务和Power BI
  5. 可视化收集的数据

Positioning

硬件模块

下图显示了用于此演示的硬件模块。

Setup

下表描述了本文中使用的硬件模块。

模块 描述
e-rt3加上F3RP70-2L(CPU模块,Ubuntu 18.04 32-位) e-rt3加控制完整的模块集。它从CPU模块访问每个模块以读取和写数据。 armhf架构包在此设备上运行。
F3AD08-6R(模拟输入模块) 模拟输入模块将收到的外部模拟数据转换为数字数据。
F3BU05-0D(基本模块) 这是连接每个模块的基础。它照顾了连接到它的模块之间的电源和通信。
F3PU20-0S(电源模块) 用于电源,电源模块连接在基本模块上。

有关硬件模块的更多信息,请参阅this page

注意:在Windows 10上执行IoT Edge模块开发和设备操作。

先决条件

完成本文的先决条件与previous article中提到的先决条件相同。此外,您必须满足列出的硬件要求。

入门

要将数据从E-RT3 Plus发送到IoT Hub并查看接收到的数据,我们必须完成以下步骤:

  1. Create Python IoT Edge module
  2. Deploy module on e-RT3 Plus
  3. Verify module operation

模块创建

创建一个用于从模拟输入模块收集数据并将其发送到IoT中心的模块,我们使用Visual Studio Code创建一个新的Python项目,然后修改代码以适合我们的要求。

Python IoT Edge数据收集模块必须从模拟输入模块的所有通道中收集数据,并将收集的数据发送到IoT Hub。此外,您必须能够从模块Twin配置数据收集频率。

创建新项目

在Visual Studio代码中创建新的Python模块的步骤与the previous article的步骤2和4中所述。

在此演示中,我们创建了一个带有以下详细信息的项目:

  • 解决方案名称:ERT3D2C
  • 模块模板:Python模块
  • 模块名称:ERT3D2CMODULE
  • 目标体系结构:ARM32V7

修改代码

创建项目后,将main.py的内容重写如下:

import os
import json
import subprocess
import signal
import datetime
import ctypes
from azure.iot.device import IoTHubModuleClient
from azure.iot.device import Message

DESIRED_KEY = 'desired'
ERT3ADD2C_KEY = 'ert3add2c'
INTERVALSEC_KEY = 'interval_sec'
STATUS_KEY = 'status'
FAM3AD_CHNUM = {'AD04': 4, 'AD08': 8}
UNIT = 0
SLOT = 2
DEFAULT_INTERVAL_SEC = 2.0
LDCONFIGEXEC = 'ldconfig'
M3LIB_PATH = '/usr/local/lib/libm3.so.1'
DEVICE_ID = os.environ['IOTEDGE_DEVICEID']
OUTPUT_NAME = os.environ['IOTEDGE_MODULEID'] + 'ToIoTHub'


class AdD2C():
    """
    Class implemented by each function that collects data from the AD module.
    """
    def __init__(self, module_client, unit, slot):
        """
        Contructor.
        """
        self.__module_client = module_client
        self.__unit = unit
        self.__slot = slot
        self.__message_no = 1
        self.__libc = ctypes.cdll.LoadLibrary(M3LIB_PATH)
        self.__libc.getM3IoName.restype = ctypes.c_char_p
        self.__chnum = self.__get_m3ad_ch_num()
        signal.signal(signal.SIGALRM, self.__signal_handler)

    def __get_m3ad_ch_num(self):
        """
        Decide on either AD08 or  AD04.
        """
        namebytes = self.__libc.getM3IoName(
            ctypes.c_int(self.__unit), ctypes.c_int(self.__slot))
        num = 0
        if namebytes is not None:
            num = FAM3AD_CHNUM.get(namebytes.decode(), 0)
        return num

    def __read_m3ad_ch_datas(self):
        """
        Obtain data from all channels of AD module.
        """
        short_arr = ctypes.c_short * self.__chnum
        ch_datas = short_arr()
        self.__libc.readM3IoRegister(
            ctypes.c_int(self.__unit),
            ctypes.c_int(self.__slot),
            ctypes.c_int(1),
            ctypes.c_int(self.__chnum),
            ch_datas)
        return ch_datas

    def __signal_handler(self, signum, frame):
        """
        Create message and send to edgeHub module.
        """
        bodyDict = dict(
            messageID=self.__message_no,
            deviceID=DEVICE_ID,
            datetime=datetime.datetime.utcnow().isoformat() + 'Z'
            )

        ch_datas = self.__read_m3ad_ch_datas()
        for index, ch_value in enumerate(ch_datas):
            bodyDict['ch' + str(index + 1)] = ch_value

        bodyStr = json.dumps(bodyDict)
        msg = Message(bodyStr, output_name=OUTPUT_NAME)
        self.__module_client.send_message(msg)
        print(bodyStr)

        self.__message_no = self.__message_no + 1

    def set_condition(self, desired):
        """
        Configure the data collection frequency settings.
        """
        if ERT3ADD2C_KEY not in desired:
            return {STATUS_KEY: False}

        interval_sec = desired[ERT3ADD2C_KEY].get(INTERVALSEC_KEY,
                                                  DEFAULT_INTERVAL_SEC)
        reported = dict()
        reported[ERT3ADD2C_KEY] = desired[ERT3ADD2C_KEY]
        if interval_sec < 0.0:
            reported[ERT3ADD2C_KEY][STATUS_KEY] = False
        else:
            signal.setitimer(signal.ITIMER_REAL, interval_sec, interval_sec)
            reported[ERT3ADD2C_KEY][STATUS_KEY] = True

        return reported


def send_ad_data():
    """
    Call each function.
    Monitor the changes in the module twin.
    """
    module_client = IoTHubModuleClient.create_from_edge_environment()
    module_client.connect()
    twin = module_client.get_twin()

    add2c = AdD2C(module_client, UNIT, SLOT)
    reported = add2c.set_condition(twin.get(DESIRED_KEY, {}))
    module_client.patch_twin_reported_properties(reported)

    while True:
        reported = add2c.set_condition(
            module_client.receive_twin_desired_properties_patch()
        )
        module_client.patch_twin_reported_properties(reported)

    module_client.disconnect()


if __name__ == "__main__":
    subprocess.run([LDCONFIGEXEC])

    send_ad_data()

注意:代码流作为代码中的注释解释。有关代码的其他信息在Python code details中描述。

构建和推动模块

重写main.py并保存代码后,您必须将Python IoT Edge Data Collection模块推到容器注册表中。

按照以下步骤构建并将Python IoT Edge数据收集模块推向容器注册表:

  1. 在Visual Studio代码窗口的左窗格上,在项目文件夹中找到deployment.template.json文件。
  2. 右键单击deployment.template.json文件,然后选择构建并按下IoT Edge解决方案

    Python IoT Edge数据收集模块是构建并将其推向容器注册表的。命令执行和执行结果的进度显示在窗口底部的终端窗格上。

注意:如果未正确配置代理设置,将Python IoT Edge数据收集模块推向容器注册表将失败。有关配置Visual Studio代码和Docker桌面代理设置的更多信息,请参考Proxy settings

部署模块

构建Python IoT Edge数据收集模块并将其推到容器注册表后,我们必须在E-RT3 Plus设备上部署。

按照以下步骤部署python iot edge数据收集模块在e-rt3 plus上,来自Azure Portal:

  1. Open Azure Portal.
  2. 导航到您创建的物联网。
  3. 在左窗格上,在设备管理下部分,单击 IoT Edge
  4. 从显示的设备ID中,选择要部署模块的目标设备的ID。

    出现设备信息。

  5. 单击设置模块

    Setup

  6. 容器注册表凭据节中,确保配置凭据。有关配置凭据的更多信息,请参阅Creating a new project的步骤7。

    注意:显示previous article中配置的PythonModuleSimulatedTemperatureSensor模块,请单击右侧的 delete 图标,将它们删除。

  7. 要配置Python IoT Edge数据收集模块的设置,请单击+add ,然后从下拉列表中选择 IoT Edge模块

    IoTEdgeModuleParameters

  8. 如下表所述配置IoT边缘模块的参数,然后单击 add

    设置 要输入的信息
    模块名称 模块名称
    图像uri 从容器注册表的存储库中获得的图像URI。有关如何获取图像URI的信息,请参阅Image URI
    重新启动策略 始终(保留默认设置)
    所需状态 跑步(保留默认设置)
    图像拉策略 空(保留默认设置)
  9. 单击容器创建选项选项卡,并在编辑器中指定选项如下。

    {
        "HostConfig": 
        {   
            "Binds": [
            "/usr/local/lib/libm3.so.1.0.1:/usr/local/lib/libm3.so.1.0.1"
            ],
            "Devices": [
            {
                "PathOnHost": "/dev/m3io",
                "PathInContainer": "/dev/m3io",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3sysctl",
                "PathInContainer": "/dev/m3sysctl",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3cpu",
                "PathInContainer": "/dev/m3cpu",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3mcom",
                "PathInContainer": "/dev/m3mcom",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3dev",
                "PathInContainer": "/dev/m3dev",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3ras",
                "PathInContainer": "/dev/m3ras",
                "CgroupPermissions": "rwm"
            },
            {
                "PathOnHost": "/dev/m3wdt",
                "PathInContainer": "/dev/m3wdt",
                "CgroupPermissions": "rwm"
            }
            ]
        }
    }
    

    CreateOptions

    有关容器创建选项的更多信息,请参阅official documentation

    有关可以配置的设置的信息,请参阅Docker documentation

    注::库信息在Binds标签中指定,设备信息在Devices标签中指定。

  10. 单击模块Twin设置选项卡并指定设置如下:

    {
        "ert3add2c": 
        {
            "interval_sec": 2
        }
    }
    

    Interval

    在这里,我们配置了2秒的间隔。这将配置Python模块,以在两秒钟的时间间隔内从模拟输入模块的所有通道收集数据。

  11. 单击应用

  12. 单击路由选项卡,并按照下表所述配置详细信息。

    名称 value
    yourmoduletoiothub 来自/消息/模块/<yourModule>/outputs/* to $ upstream

    Routes

    在这里,我们配置了将数据从Python IoT Edge Data Collection模块发送到IoT Hub的路由。

    注意:如果显示previous article中配置的路线,请单击右侧的 delete 图标,将它们删除。

  13. 单击评论 +创建并验证配置信息。

    在屏幕的左上角,显示消息“验证”。

  14. 在验证配置信息后,在页面的左下角,单击 create

    出现设备设置页面,并显示带有每个模块状态的模块列表。

    ModuleStatus

  15. 验证以下信息:

    • IoT Edge运行时响应必须显示为200 - OK
    • 所有模块的运行时状态([Edgeagent],[EdgeHub]和您部署的模块[yourmodule])必须显示为running

    注意:通常需要一些时间来查看部署的状态。如果不满足上述任何条件,请单击刷新查看最新信息。

验证模块操作

要验证模块操作,我们必须观察到以下结果:

  1. View telemetry data in IoT Hub

    验证在IoT Hub中收到Python IoT Edge数据收集模块发送的遥测数据。

  2. View changes in operation corresponding to Module twin updates

    要验证对模块Twin的更新反映在IoT Hub接收到的消息中,我们执行以下操作:

    1. 增加数据收集的频率并观察IoT中心处的相应变化。
    2. 将数据收集的频率设置为零。这停止了​​数据传输。

查看遥测数据

您可以以previous article中描述的方式验证Python IoT Edge数据收集模块的操作。

如果收到的遥测数据的格式如下,则Python IoT Edge数据收集模块正常运行。

    {
        "body": {
            "messageID": 2034,
            "deviceID": "test_ert3_f3rp70",
            "datetime": "2021-01-26T03:50:58.445146Z",
            "ch1": -1,
            "ch2": 8,
            "ch3": 1,
            "ch4": 2,
            "ch5": 0,
            "ch6": 1,
            "ch7": 3,
            "ch8": 1
        },
        "enqueuedTime": "2021-01-26T03:50:58.476Z",
        "properties": {}
    }

注意:当使用8通道F3AD08时,传输了8个通道的数据。

更新模块twin并验证操作

我们可以从Azure Portal更新模块Twin,并验证模块操作是否相应地更改。

请按照以下步骤更新模块twin:

  1. 打开Azure portal并导航到您创建的IoT中心。
  2. 在左窗格上,在设备管理下部分,单击 IoT Edge
  3. 从显示的设备ID中,选择要部署模块的设备的ID。

    出现设备信息。

  4. 单击设置模块,选择要修改的IoT边缘模块。

    出现IoT边缘模块屏幕。

  5. 单击模块双设置选项卡。

    显示模块的双信息。

  6. 根据需要在文本框中更新模块双信息。验证更新的信息,如果没有问题,请单击评论 +创建

  • 修改数据收集的频率

    更新interval_sec的值以更改数据收集的频率。例如,我们将数据收集频率更新为5秒钟,如下所示:

    {
    "ert3add2c": {
        "interval_sec": 5
         }
    }
  • 停止数据收集

    如果interval_sec的值设置为零,则停止数据收集,并且不会将消息发送到IoT Hub。

     {
     "ert3add2c": {
         "interval_sec": 0
          }
    }

使用更新创建并部署该模块。部署完成后,请按以下方式验证操作:
1.如果您增加或降低了数据收集频率,则在物联网中心收到的遥测数据会发生相应的变化。
2.如果您停止数据收集,则在IoT中心未收到任何消息。

注意:如果要重新启动数据收集,请将interval_sec的值更改为大于1的值。

结论

来自模拟输入模块的数据是由E-RT3 Plus设备中部署的Python模块接收的。随后,可以在Azure IoT Hub中查看相同的数据,证明创建的Python模块是按预期运行的。在下一篇文章中,我们将创建一个物联网边缘模块,该模块将数据写入连接到E-RT3 Plus设备的模拟输出模块中。

附录

图像URI

要获得图像URI,请按照以下步骤:

  1. 打开Azure Portal并导航到容器注册表。
  2. 在左窗格上,在服务下类别,单击存储库
  3. 从显示的存储库列表中,单击要部署的存储库ID。
  4. 从显示的标签中,单击要部署的标签。

    注意:标签以<version_number>-<target_device>格式显示。

  5. 确认标签中的目标设备显示为arm32v7

    显示存储库详细信息。在 docker pull命令框中,main.py之后显示的内容是图像uri。

    ImageURI

    注意:图像URI处于格式<registry name>.azurecr.io/<module name>:<tag>

Python代码详细信息

代码的流程在Python代码中的注释中被解释为注释。在这里,我们描述了其他详细信息:

以下库是导入的:

  1. ctypes

    ctypes库提供C兼容的数据类型,并允许在DLL或共享库中调用功能。由于E-RT3 Plus设备在C语言上运行的功能,我们导入ctypes库。

        import ctypes
    
  2. IoTHubModuleClient

    IoTHubModuleClient库是Azure IoT Hub SDK的一部分。这是一个同步模块客户端,可连接到Azure IoT轮毂或Azure IoT Edge实例。有关IoTHubModuleClient库的更多信息,请单击here

    from azure.iot.device import IoTHubModuleClient
    
  3. Message

    Message库是Azure IoT Hub SDK的一部分。它代表了与Iothub或来自Iothub的消息。有关Message库的更多信息,请单击here

    from azure.iot.device import Message
    
  4. 访问模拟输入模块的库不包含在Python Iot Edge数据集模块中,而是绑定到CPU模块(host)库并使用的。

定义

以下定义提供了python ioT边缘数据收集模块中e-rt3 plus的符号链接的值。

M3LIB_PATH = '/usr/local/lib/libm3.so.1'

E-RT3加上功能

Python模块使用以下E-RT3加上功能:

  1. getM3IoName

    此功能通过指定单元和插槽ID来获得模块ID。

    函数定义如下:

    char* getM3IoName (int unit, int slot);
    
  2. readM3IoRegister

    此函数通过指定单元,插槽,频道读取I/O寄存器的数据,您需要从中读取数据,要读取的频道数量以及数据存储位置。

    函数定义如下:

    int readM3IoRegister(int unit, int slot, int pos, int num, unsigned short *data);
    

其他功能

subprocess.run([LDCONFIGEXEC])

此功能用于将F3RP70-2L的/usr/local/lib/libm3.so.1.0.1绑定到Python IoT边缘数据收集模块的/usr/local/lib/libm3.so.1.0.1

它生成了库的符号链接,并在主函数的开头调用,以在部署python iot edge数据集合模块时在e-rt3 plus中使用库。

有效载荷格式

以下格式创建了发送数据的有效载荷:

{
"body": {
    "messageID": 1,
    "deviceID": "{deviceID}",
    "datetime": "2020-01-01T00:00:00.000Z",
    "ch1": 123,
    "ch2": 234,
    "ch3": 345,
    "ch4": 456,
    "ch5": 567,
    "ch6": 678,
    "ch7": 789,
    "ch8": 890
    }
}

在这里,

messageID是消息的ID。消息ID是从1开始的数字序列生成的。

deviceID是指部署Python IoT Edge数据收集模块的目标设备的ID,

datetime是指CPU模块获取数据的时间,

ch1 -ch8是指从每个通道中获取的数据。

参考

  1. Real-time OS controller e-RT3 Plus F3RP70-2L
  2. Azure Certified Device catalog
  3. Visualize real-time sensor data from your Azure IoT hub in a web application
  4. Tutorial: Visualize real-time sensor data from Azure IoT Hub using Power BI
  5. Tutorial: Develop IoT Edge modules with Linux containers
  6. Tutorial: Develop and deploy a Python IoT Edge module using Linux containers
  7. Summary and usage methods of Azure IoT Hub SDK