IoT 中心模块标识和模块孪生 (.NET) 入门

模块标识和模块孪生类似于 Azure IoT 中心设备标识和设备孪生,但提供更精细的粒度。 Azure IoT 中心设备标识和设备孪生允许后端应用程序配置设备并提供设备条件的可见性,而模块标识和模块孪生为设备的各个组件提供这些功能。 在支持多个组件的设备上(例如操作系统设备或固件设备),模块标识和模块孪生允许每个部件拥有独立的配置和条件。

注意

本文所述的功能只能用于 IoT 中心的标准层。 有关 IoT 中心基本层和标准/免费层的详细信息,请参阅选择适合你的解决方案的 IoT 中心层

在本文结束时,会获得两个 .NET 控制台应用:

  • CreateIdentities:创建设备标识、模块标识和相关的安全密钥,以连接设备和模块客户端。

  • UpdateModuleTwinReportedProperties:将更新的模块孪生、报告属性发送到 IoT 中心。

注意

有关可用于生成设备和后端应用的 SDK 工具的详细信息,请参阅 Azure IoT SDK

先决条件

  • Visual Studio。

  • Azure 订阅中的 IoT 中心。 如果还没有中心,则可以按照创建 IoT 中心中的步骤进行操作。

模块身份验证

你可以使用对称密钥或 X.509 证书对模块标识进行身份验证。 对于 X.509 证书身份验证,模块的证书的公用名 (CN) 格式必须 如 CN=<deviceid>/<moduleid> 所示。 例如:

openssl req -new -key d1m1.key.pem -out d1m1.csr -subj "/CN=device01\/module01"

获取 IoT 中心连接字符串

在本文中,将创建一个后端服务,该服务在标识注册表中添加一个设备,然后向该设备添加一个模块。 你的服务需要“注册表写入”权限。 默认情况下,每个 IoT 中心都使用名为 registryReadWrite 的共享访问策略创建,该策略授予此权限。

若要获取 registryReadWrite策略的 IoT 中心连接字符串,请执行以下步骤:

  1. Azure 门户中,选择“资源组”。 选择中心所在的资源组,然后从资源列表中选择中心。

  2. 在中心的左侧窗格上,选择“共享访问策略”。

  3. 在策略列表中,选择“registryReadWrite”策略。

  4. 复制“主连接字符串”并保存该值。

    显示如何检索连接字符串的屏幕截图

有关 IoT 中心共享访问策略和权限的详细信息,请参阅访问控制和权限

重要

本文介绍使用共享访问签名连接到服务的步骤。 虽然可使用此身份验证方法进行测试和评估,但使用 Microsoft Entra ID 或托管标识对设备进行身份验证是一种更安全的方法。

创建模块标识

本部分将创建一个 .NET 控制台应用,用于在中心的标识注册表中创建设备标识和模块标识。 设备或模块无法连接到中心,除非它在标识注册表中具有条目。 有关详细信息,请参阅 IoT 中心开发人员指南的“标识注册表”部分

运行此控制台应用时,它会为设备和模块生成唯一的 ID 和密钥。 设备和模块在向 IoT 中心发送设备到云的消息时,使用这些值来标识自身。 ID 区分大小写。

  1. 打开 Visual Studio,选择“新建项目”。

  2. 在“创建新项目”中,选择“控制台应用(.NET Framework)”

  3. 选择“下一步”,打开“配置新项目”。 将项目命名为 CreateIdentities,然后选择“下一步”。

    显示带有“CreateIdentities”的“配置新项目”弹出窗口的屏幕截图。

  4. 保留默认 .NET Framework 选项,然后选择“创建”以创建项目。

  5. 在 Visual Studio 中,打开“工具”>“NuGet 包管理器”>“管理解决方案的 NuGet 包”。 选择“浏览”选项卡。

  6. 搜索 Microsoft.Azure.Devices。 选择它,然后选择“安装”。

    安装 Azure IoT 中心 .NET 服务 SDK 当前版本

  7. Program.cs 文件顶部添加以下 using 语句:

    using Microsoft.Azure.Devices;
    using Microsoft.Azure.Devices.Common.Exceptions;
    
  8. 将以下字段添加到 Program 类。 将占位符值替换为在上一部分为中心创建的 IoT 中心连接字符串。

    const string connectionString = "<replace_with_iothub_connection_string>";
    const string deviceID = "myFirstDevice";
    const string moduleID = "myFirstModule";
    
  9. 将以下代码添加到 Main 类。

    static void Main(string[] args)
    {
        AddDeviceAsync().Wait();
        AddModuleAsync().Wait();
    }
    
  10. 将以下方法添加到 Program 类:

    private static async Task AddDeviceAsync()
    {
       RegistryManager registryManager = 
         RegistryManager.CreateFromConnectionString(connectionString);
       Device device;
    
       try
       {
           device = await registryManager.AddDeviceAsync(new Device(deviceID));
       }
       catch (DeviceAlreadyExistsException)
        {
            device = await registryManager.GetDeviceAsync(deviceID);
        }
    
        Console.WriteLine("Generated device key: {0}", 
          device.Authentication.SymmetricKey.PrimaryKey);
    }
    
    private static async Task AddModuleAsync()
    {
        RegistryManager registryManager = 
          RegistryManager.CreateFromConnectionString(connectionString);
        Module module;
    
        try
        {
            module = 
              await registryManager.AddModuleAsync(new Module(deviceID, moduleID));
        }
        catch (ModuleAlreadyExistsException)
        {
            module = await registryManager.GetModuleAsync(deviceID, moduleID);
        }
    
        Console.WriteLine("Generated module key: {0}", module.Authentication.SymmetricKey.PrimaryKey);
    }
    

    AddDeviceAsync 方法会创建 ID 为 myFirstDevice 的设备标识。 如果该设备 ID 已在标识注册表中,代码就只检索现有的设备信息。 然后,应用程序会显示该标识的主密钥。 在模拟设备应用中使用此密钥连接到中心。

    AddModuleAsync 方法在设备 myFirstDevice 下创建 ID 为 myFirstModule 的模块标识。 如果该模块 ID 已在标识注册表中,代码就只检索现有的模块信息。 然后,应用程序会显示该标识的主密钥。 在模拟模块应用中使用此密钥连接到中心。

    重要

    收集的日志中可能会显示设备 ID 用于客户支持和故障排除,因此,在为日志命名时,请务必避免包含任何敏感信息。

  11. 运行此应用并记下设备密钥和模块密钥。

注意

IoT 中心标识注册表只存储设备和模块标识,以实现对中心的安全访问。 标识注册表存储用作安全凭据的设备 ID 和密钥。 标识注册表还为每个设备存储启用/禁用标志,该标志可以用于禁用对该设备的访问。 如果应用需要存储其他特定于设备的元数据,则应使用特定于应用程序的存储。 没有针对模块标识的“已启用/已禁用”标记。 有关详细信息,请参阅 IoT 中心开发人员指南

使用 .NET 设备 SDK 更新模块孪生

现在让我们从模拟设备与云进行通信。 创建模块标识后,在 IoT 中心内隐式创建模块孪生。 在本节中,将在更新模块孪生报告属性的模拟设备上创建 .NET 控制台应用。

若要检索模块连接字符串,请导航到 IoT 中心,然后选择“设备”。 查找并选择“myFirstDevice”将其打开,然后选择“myFirstModule”将其打开。 在“模块标识详细信息”中,复制“连接字符串(主密钥)”并将其保存到控制台应用。

重要

本文包括使用共享访问签名(也称为对称密钥身份验证)连接设备的步骤。 此身份验证方法便于测试和评估,但使用 X.509 证书对设备进行身份验证是一种更安全的方法。

  1. 在 Visual Studio 中将新项目添加到解决方案,只需选择 “文件”>“新建”>“项目” 即可。 在“创建新项目”中,选择“控制台应用(.NET Framework)”,然后选择“下一步”。

  2. 在“配置新项目”中,将项目命名为 UpdateModuleTwinReportedProperties,然后选择“下一步”。

    显示“配置新项目”弹出窗口的屏幕截图。

  3. 保留默认 .NET Framework 选项,然后选择“创建”以创建项目。

  4. 在 Visual Studio 中,打开“工具”>“NuGet 包管理器”>“管理解决方案的 NuGet 包”。 选择“浏览”选项卡。

  5. 搜索并选择 Microsoft.Azure.Devices.Client,然后选择“安装”。

    显示已选中“Microsoft.Azure.Devices.Client”并突出显示“安装”按钮的屏幕截图。

  6. Program.cs 文件顶部添加以下 using 语句:

    using Microsoft.Azure.Devices.Client;
    using Microsoft.Azure.Devices.Shared;
    using System.Threading.Tasks;
    using Newtonsoft.Json;
    
  7. 将以下字段添加到 Program 类。 将占位符值替换为模块连接字符串。

    private const string ModuleConnectionString = "<Your module connection string>";
    private static ModuleClient Client = null;
    static void ConnectionStatusChangeHandler(ConnectionStatus status, 
      ConnectionStatusChangeReason reason)
    {
        Console.WriteLine("Connection Status Changed to {0}; the reason is {1}", 
          status, reason);
    }
    
  8. 将以下方法“OnDesiredPropertyChanged”添加到“Program”类 :

    private static async Task OnDesiredPropertyChanged(TwinCollection desiredProperties, 
      object userContext)
        {
            Console.WriteLine("desired property change:");
            Console.WriteLine(JsonConvert.SerializeObject(desiredProperties));
            Console.WriteLine("Sending current time as reported property");
            TwinCollection reportedProperties = new TwinCollection
            {
                ["DateTimeLastDesiredPropertyChangeReceived"] = DateTime.Now
            };
    
            await Client.UpdateReportedPropertiesAsync(reportedProperties).ConfigureAwait(false);
        }
    
  9. Main 方法中添加以下行:

    static void Main(string[] args)
    {
        Microsoft.Azure.Devices.Client.TransportType transport = 
          Microsoft.Azure.Devices.Client.TransportType.Amqp;
    
        try
        {
            Client = 
              ModuleClient.CreateFromConnectionString(ModuleConnectionString, transport);
            Client.SetConnectionStatusChangesHandler(ConnectionStatusChangeHandler);
            Client.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChanged, null).Wait();
    
            Console.WriteLine("Retrieving twin");
            var twinTask = Client.GetTwinAsync();
            twinTask.Wait();
            var twin = twinTask.Result;
            Console.WriteLine(JsonConvert.SerializeObject(twin.Properties)); 
    
            Console.WriteLine("Sending app start time as reported property");
            TwinCollection reportedProperties = new TwinCollection();
            reportedProperties["DateTimeLastAppLaunch"] = DateTime.Now;
    
            Client.UpdateReportedPropertiesAsync(reportedProperties);
        }
        catch (AggregateException ex)
        {
            Console.WriteLine("Error in sample: {0}", ex);
        }
    
        Console.WriteLine("Waiting for Events.  Press enter to exit...");
        Console.ReadLine();
        Client.CloseAsync().Wait();
    }
    

    现在你了解了如何检索模块孪生和借助 AMQP 协议更新报告属性。

  10. 也可将这些语句添加到 Main 方法,以便将事件从模块发送到 IoT 中心。 将这些行置于 try catch 块下。

    Byte[] bytes = new Byte[2];
    bytes[0] = 0;
    bytes[1] = 1;
    var sendEventsTask = Client.SendEventAsync(new Message(bytes));
    sendEventsTask.Wait();
    Console.WriteLine("Event sent to IoT Hub.");
    

运行应用

现在可以运行这些应用了。

  1. 在 Visual Studio 的“解决方案资源管理器”中,右键单击解决方案并选择“设置启动项目”。

  2. 在“通用属性”下选择“启动项目”。

  3. 选择“多个启动项目”,接着选择“启动”作为应用的操作,然后选择“确定”接受所做的更改

  4. F5 启动应用。

后续步骤

若要继续了解 IoT 中心入门知识并浏览其他 IoT 方案,请参阅: