IoT 中心模块标识和模块孪生 (C) 入门Get started with IoT Hub module identity and module twin (C)

备注

模块标识和模块孪生类似于 Azure IoT 中心设备标识和设备孪生,但提供更精细的粒度。Module identities and module twins are similar to Azure IoT Hub device identity and device twin, but provide finer granularity. Azure IoT 中心设备标识和设备孪生允许后端应用程序配置设备并提供设备条件的可见性,而模块标识和模块孪生为设备的各个组件提供这些功能。While Azure IoT Hub device identity and device twin enable the back-end application to configure a device and provides visibility on the device’s conditions, a module identity and module twin provide these capabilities for individual components of a device. 在支持多个组件的设备上(例如基于操作系统的设备或固件设备),它允许每个部件拥有独立的配置和条件。On capable devices with multiple components, such as operating system based devices or firmware devices, it allows for isolated configuration and conditions for each component.

在本教程结束时,会创建两个 C 应用:At the end of this tutorial, you have two C apps:

  • CreateIdentities,用于创建设备标识、模块标识和相关的安全密钥,以连接设备和模块客户端。CreateIdentities, which creates a device identity, a module identity and associated security key to connect your device and module clients.
  • UpdateModuleTwinReportedProperties,用于将更新的模块孪生报告属性发送到 IoT 中心。UpdateModuleTwinReportedProperties, which sends updated module twin reported properties to your IoT Hub.

备注

有关 Azure IoT SDK 的信息(可以使用这些 SDK 构建可在设备和解决方案后端上运行的应用程序),请参阅 Azure IoT SDKFor information about the Azure IoT SDKs that you can use to build both applications to run on devices, and your solution backend, see Azure IoT SDKs.

先决条件Prerequisites

  • 有效的 Azure 帐户。An active Azure account. (如果没有帐户,只需几分钟即可创建一个[试用帐户][lnk-free-trial]。)(If you don't have an account, you can create a [trial account][lnk-free-trial] in just a couple of minutes.)

  • 最新 Azure IoT C SDKThe latest Azure IoT C SDK.

创建 IoT 中心Create an IoT hub

此部分介绍如何使用 Azure 门户创建 IoT 中心。This section describes how to create an IoT hub using the Azure portal.

  1. 登录到 Azure 门户Sign in to the Azure portal.

  2. 从 Azure 主页中选择“+ 创建资源”按钮,然后在“搜索市场”字段中输入“IoT 中心”。 From the Azure homepage, select the + Create a resource button, and then enter IoT Hub in the Search the Marketplace field.

  3. 在搜索结果中选择“IoT 中心”,然后选择“创建” 。Select IoT Hub from the search results, and then select Create.

  4. 在“基本信息”选项卡上,按如下所示填写字段: On the Basics tab, complete the fields as follows:

    • 订阅:选择要用于中心的订阅。Subscription: Select the subscription to use for your hub.

    • 资源组:选择一个资源组或新建一个资源组。Resource Group: Select a resource group or create a new one. 若要新建资源组,请选择“新建”并填写要使用的名称。 To create a new one, select Create new and fill in the name you want to use. 若要使用现有的资源组,请选择它。To use an existing resource group, select that resource group. 有关详细信息,请参阅管理 Azure 资源管理器资源组For more information, see Manage Azure Resource Manager resource groups.

    • 区域:选择中心所在的区域。Region: Select the region in which you want your hub to be located. 选择最靠近你的位置。Select the location closest to you.

    • IoT 中心名称:输入中心的名称。IoT Hub Name: Enter a name for your hub. 此名称必须全局唯一。This name must be globally unique. 如果输入的名称可用,会显示一个绿色复选标记。If the name you enter is available, a green check mark appears.

    重要

    IoT 中心将公开为 DNS 终结点,因此,命名时请务必避免包含任何敏感信息。The IoT hub will be publicly discoverable as a DNS endpoint, so make sure to avoid any sensitive information while naming it.

    在 Azure 门户中创建中心

  5. 在完成时选择“下一步: 大小和规模”,以继续创建中心。Select Next: Size and scale to continue creating your hub.

    使用 Azure 门户为新的中心设置大小和规模

    在此屏幕中可以设置以下值:This screen allows you to set the following values:

    • 定价和缩放层:选择的层。Pricing and scale tier: Your selected tier. 可以根据你需要的功能数以及每天通过解决方案发送的消息数从多个层级中进行选择。You can choose from several tiers, depending on how many features you want and how many messages you send through your solution per day. 免费层适用于测试和评估。The free tier is intended for testing and evaluation. 允许 500 台设备连接到中心,每天最多可传输 8,000 条消息。It allows 500 devices to be connected to the hub and up to 8,000 messages per day. 每个 Azure 订阅可以在免费层中创建一个 IoT 中心。Each Azure subscription can create one IoT Hub in the free tier.

    • IoT 中心单元:每个单位每日允许的消息数取决于中心的定价层。IoT Hub units: The number of messages allowed per unit per day depends on your hub's pricing tier. 例如,如果希望中心支持 700,000 条消息引入,请选择两个 S1 层单位。For example, if you want the hub to support ingress of 700,000 messages, you choose two S1 tier units. 有关其他层选项的详细信息,请参阅选择合适的 IoT 中心层For details about the other tier options, see Choosing the right IoT Hub tier.

    • 高级设置 > 设备到云的分区:此属性将设备到云消息与这些消息的同步读取器数目相关联。Advanced Settings > Device-to-cloud partitions: This property relates the device-to-cloud messages to the number of simultaneous readers of the messages. 大多数中心只需要 4 个分区。Most hubs need only four partitions.

  6. 对于本文,请接受默认选项,然后选择“查看 + 创建”以查看所做的选择。 For this article, accept the default choices, and then select Review + create to review your choices. 会显示类似于以下的屏幕。You see something similar to this screen.

    查看用于创建新中心的信息

  7. 选择“创建”以创建新的中心 。Select Create to create your new hub. 创建中心需要几分钟时间。Creating the hub takes a few minutes.

获取 IoT 中心连接字符串Get the IoT hub connection string

在本文中,将创建一个后端服务,该服务在标识注册表中添加一个设备,然后向该设备添加一个模块。In this article, you create a back-end service that adds a device in the identity registry and then adds a module to that device. 你的服务需要“注册表写入” 权限。Your service requires the registry write permission. 默认情况下,每个 IoT 中心都使用名为 registryReadWrite 的共享访问策略创建,该策略授予此权限。By default, every IoT hub is created with a shared access policy named registryReadWrite that grants this permission.

若要获取 registryReadWrite 策略的 IoT 中心连接字符串,请执行以下步骤:To get the IoT Hub connection string for the registryReadWrite policy, follow these steps:

  1. Azure 门户中,选择“资源组” 。In the Azure portal, select Resource groups. 选择中心所在的资源组,然后从资源列表中选择中心。Select the resource group where your hub is located, and then select your hub from the list of resources.

  2. 在中心的左侧窗格中,选择“共享访问策略” 。On the left-side pane of your hub, select Shared access policies.

  3. 从策略列表中选择“registryReadWrite” 策略。From the list of policies, select the registryReadWrite policy.

  4. 在“共享访问密钥” 下,选择“连接字符串 - 主密钥” 所对应的“复制”图标并保存该值。Under Shared access keys, select the copy icon for the Connection string -- primary key and save the value.

    显示如何检索连接字符串

有关 IoT 中心共享访问策略和权限的详细信息,请参阅访问控制和权限For more information about IoT Hub shared access policies and permissions, see Access control and permissions.

在 IoT 中心中创建设备标识和模块标识Create a device identity and a module identity in IoT Hub

本部分将创建一个 C 应用,用于在 IoT 中心的标识注册表中创建设备标识和模块标识。In this section, you create a C app that creates a device identity and a module identity in the identity registry in your IoT hub. 设备或模块无法连接到 IoT 中心,除非它在标识注册表中具有条目。A device or module cannot connect to IoT hub unless it has an entry in the identity registry. 有关详细信息,请参阅 IoT 中心开发人员指南中的“标识注册表” 部分。For more information, see the Identity registry section of the IoT Hub developer guide. 运行此控制台应用时,它会为设备和模块生成唯一的 ID 和密钥。When you run this console app, it generates a unique ID and key for both device and module. 设备和模块在向 IoT 中心发送设备到云的消息时,使用这些值来标识自身。Your device and module use these values to identify itself when it sends device-to-cloud messages to IoT Hub. ID 区分大小写。The IDs are case-sensitive.

将以下代码添加到 C 文件:Add the following code to your C file:

#include <stdio.h>
#include <stdlib.h>

#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/platform.h"

#include "iothub_service_client_auth.h"
#include "iothub_registrymanager.h"

static const char* hubConnectionString ="[your hub's connection string]"; // modify

static void createDevice(IOTHUB_REGISTRYMANAGER_HANDLE 
  iotHubRegistryManagerHandle, const char* deviceId)
{
    IOTHUB_REGISTRY_DEVICE_CREATE_EX deviceCreateInfo;
    IOTHUB_REGISTRYMANAGER_RESULT result;
    
    (void)memset(&deviceCreateInfo, 0, sizeof(deviceCreateInfo));
    deviceCreateInfo.version = 1;
    deviceCreateInfo.deviceId = deviceId;
    deviceCreateInfo.primaryKey = "";
    deviceCreateInfo.secondaryKey = "";
    deviceCreateInfo.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_SPK;
    
    IOTHUB_DEVICE_EX deviceInfoEx;
    memset(&deviceInfoEx, 0, sizeof(deviceInfoEx));
    deviceInfoEx.version = 1;
    
    // Create device
    result = IoTHubRegistryManager_CreateDevice_Ex(iotHubRegistryManagerHandle, &deviceCreateInfo, &deviceInfoEx);
    if (result == IOTHUB_REGISTRYMANAGER_OK)
    {
        (void)printf("IoTHubRegistryManager_CreateDevice: Device has been created successfully: deviceId=%s, primaryKey=%s\n", deviceInfoEx.deviceId, deviceInfoEx.primaryKey);
    }
    else if (result == IOTHUB_REGISTRYMANAGER_DEVICE_EXIST)
    {
        (void)printf("IoTHubRegistryManager_CreateDevice: Device already exists\n");
    }
    else if (result == IOTHUB_REGISTRYMANAGER_ERROR)
    {
        (void)printf("IoTHubRegistryManager_CreateDevice failed\n");
    }
    // You will need to Free the returned device information after it was created
    IoTHubRegistryManager_FreeDeviceExMembers(&deviceInfoEx);
}

static void createModule(IOTHUB_REGISTRYMANAGER_HANDLE iotHubRegistryManagerHandle, const char* deviceId, const char* moduleId)
{
    IOTHUB_REGISTRY_MODULE_CREATE moduleCreateInfo;
    IOTHUB_REGISTRYMANAGER_RESULT result;    
    
    (void)memset(&moduleCreateInfo, 0, sizeof(moduleCreateInfo));
    moduleCreateInfo.version = 1;
    moduleCreateInfo.deviceId = deviceId;
    moduleCreateInfo.moduleId = moduleId;
    moduleCreateInfo.primaryKey = "";
    moduleCreateInfo.secondaryKey = "";
    moduleCreateInfo.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_SPK;
    
    IOTHUB_MODULE moduleInfo;
    memset(&moduleInfo, 0, sizeof(moduleInfo));
    moduleInfo.version = 1;
    
    // Create module
    result = IoTHubRegistryManager_CreateModule(iotHubRegistryManagerHandle, &moduleCreateInfo, &moduleInfo);
    if (result == IOTHUB_REGISTRYMANAGER_OK)
    {
        (void)printf("IoTHubRegistryManager_CreateModule: Module has been created successfully: deviceId=%s, moduleId=%s, primaryKey=%s\n", moduleInfo.deviceId, moduleInfo.moduleId, moduleInfo.primaryKey);
    }
    else if (result == IOTHUB_REGISTRYMANAGER_DEVICE_EXIST)
    {
        (void)printf("IoTHubRegistryManager_CreateModule: Module already exists\n");
    }
    else if (result == IOTHUB_REGISTRYMANAGER_ERROR)
    {
        (void)printf("IoTHubRegistryManager_CreateModule failed\n");
    }
    // You will need to Free the returned module information after it was created
    IoTHubRegistryManager_FreeModuleMembers(&moduleInfo);
}

int main(void)
{
    (void)platform_init();

    const char* deviceId = "myFirstDevice";
    const char* moduleId = "myFirstModule";
    IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iotHubServiceClientHandle = NULL;
    IOTHUB_REGISTRYMANAGER_HANDLE iotHubRegistryManagerHandle = NULL;

    if ((iotHubServiceClientHandle = IoTHubServiceClientAuth_CreateFromConnectionString(hubConnectionString)) == NULL)
    {
        (void)printf("IoTHubServiceClientAuth_CreateFromConnectionString failed\n");
    }
    else if ((iotHubRegistryManagerHandle = IoTHubRegistryManager_Create(iotHubServiceClientHandle)) == NULL)
    {
        (void)printf("IoTHubServiceClientAuth_CreateFromConnectionString failed\n");
    }
    else
    {
        createDevice(iotHubRegistryManagerHandle, deviceId);
        createModule(iotHubRegistryManagerHandle, deviceId, moduleId);
    }

    if (iotHubRegistryManagerHandle != NULL)
    {
        (void)printf("Calling IoTHubRegistryManager_Destroy...\n");
        IoTHubRegistryManager_Destroy(iotHubRegistryManagerHandle);
    }

    if (iotHubServiceClientHandle != NULL)
    {
        (void)printf("Calling IoTHubServiceClientAuth_Destroy...\n");
        IoTHubServiceClientAuth_Destroy(iotHubServiceClientHandle);
    }
    
    platform_deinit();
    return 0;
}

此应用在设备“myFirstDevice”下创建 ID 为“myFirstDevice”的设备标识,以及 ID 为“myFirstModule”的模块标识 。This app creates a device identity with ID myFirstDevice and a module identity with ID myFirstModule under device myFirstDevice. (如果该模块 ID 已在标识注册表中,代码就只检索现有的模块信息。)然后,应用程序会显示该标识的主密钥。(If that module ID already exists in the identity registry, the code simply retrieves the existing module information.) The app then displays the primary key for that identity. 在模拟模块应用中使用此密钥连接到 IoT 中心。You use this key in the simulated module app to connect to your IoT hub.

备注

IoT 中心标识注册表只存储设备和模块标识,以启用对 IoT 中心的安全访问。The IoT Hub identity registry only stores device and module identities to enable secure access to the IoT hub. 标识注册表存储用作安全凭据的设备 ID 和密钥。The identity registry stores device IDs and keys to use as security credentials. 标识注册表还为每个设备存储启用/禁用标志,该标志可以用于禁用对该设备的访问。The identity registry also stores an enabled/disabled flag for each device that you can use to disable access for that device. 如果应用程序需要存储其他特定于设备的元数据,则应使用特定于应用程序的存储。If your application needs to store other device-specific metadata, it should use an application-specific store. 没有针对模块标识的“已启用/已禁用”标记。There is no enabled/disabled flag for module identities. 有关详细信息,请参阅 IoT 中心开发人员指南For more information, see IoT Hub developer guide.

使用 C 设备 SDK 更新模块孪生Update the module twin using C device SDK

本节将在更新模块孪生报告属性的模拟设备上创建 C 应用。In this section, you create a C app on your simulated device that updates the module twin reported properties.

  1. 获取模块连接字符串 -- 现在,如果登录到 [Azure 门户][lnk-portal]。Get your module connection string -- now if you login to [Azure portal][lnk-portal]. 导航到 IoT 中心并单击 IoT 设备。Navigate to your IoT Hub and click IoT Devices. 查找并打开 myFirstDevice,可以看到 myFirstModule 已成功创建。Find myFirstDevice, open it and you see myFirstModule was successfully created. 复制模块连接字符串。Copy the module connection string. 下一步将需要它。It is needed in the next step.

    Azure 门户模块详细信息

  2. 创建 UpdateModuleTwinReportedProperties 应用Create UpdateModuleTwinReportedProperties app

    将以下内容添加到 C 文件:Add the following to your C file:

    #include <stdio.h>
    #include <stdlib.h>
    
    #include "azure_c_shared_utility/crt_abstractions.h"
    #include "azure_c_shared_utility/threadapi.h"
    #include "azure_c_shared_utility/platform.h"
    
    #include "iothub_service_client_auth.h"
    #include "iothub_devicetwin.h"
    
    const char* deviceId = "bugbash-test-2";
    const char* moduleId = "module-id-1";
    static const char* hubConnectionString ="[your hub's connection string]"; // modify
    const char* testJson = "{\"properties\":{\"desired\":{\"integer_property\": b-1234, \"string_property\": \"abcd\"}}}";
    
    int main(void)
    {
        (void)platform_init();
    
        IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iotHubServiceClientHandle = NULL;
        IOTHUB_SERVICE_CLIENT_DEVICE_TWIN_HANDLE iothubDeviceTwinHandle = NULL;
    
        if ((iotHubServiceClientHandle = IoTHubServiceClientAuth_CreateFromConnectionString(moduleConnectionString)) == NULL)
        {
            (void)printf("IoTHubServiceClientAuth_CreateFromConnectionString failed\n");
        }
        else if ((iothubDeviceTwinHandle = IoTHubDeviceTwin_Create(iotHubServiceClientHandle)) == NULL)
        {
            (void)printf("IoTHubServiceClientAuth_CreateFromConnectionString failed\n");
        }
        else
        {
            char *result = IoTHubDeviceTwin_UpdateModuleTwin(iothubDeviceTwinHandle, deviceId, moduleId, testJson);
            printf("IoTHubDeviceTwin_UpdateModuleTwin returned %s\n", result);
        }
    
        if (iothubDeviceTwinHandle != NULL)
        {
            (void)printf("Calling IoTHubDeviceTwin_Destroy...\n");
            IoTHubDeviceTwin_Destroy(iothubDeviceTwinHandle);
        }
    
        if (iotHubServiceClientHandle != NULL)
        {
            (void)printf("Calling IoTHubServiceClientAuth_Destroy...\n");
            IoTHubServiceClientAuth_Destroy(iotHubServiceClientHandle);
        }
    
        platform_deinit();
        return 0;
    }
    

此代码示例演示如何检索模块孪生并更新报告的属性。This code sample shows you how to retrieve the module twin and update reported properties.

在设备端获取更新Get updates on the device side

除了上述代码,还可添加以下代码块以在设备上获取孪生更新消息。In addition to the above code, you can add below code block to get the twin update message on your device.

#include <stdio.h>
#include <stdlib.h>

#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/macro_utils.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/platform.h"
#include "iothub_module_client_ll.h"
#include "iothub_client_options.h"
#include "iothub_message.h"

// The protocol you wish to use should be uncommented
//
//#define SAMPLE_MQTT
//#define SAMPLE_MQTT_OVER_WEBSOCKETS
#define SAMPLE_AMQP
//#define SAMPLE_AMQP_OVER_WEBSOCKETS
//#define SAMPLE_HTTP

#ifdef SAMPLE_MQTT
    #include "iothubtransportmqtt.h"
#endif // SAMPLE_MQTT
#ifdef SAMPLE_MQTT_OVER_WEBSOCKETS
    #include "iothubtransportmqtt_websockets.h"
#endif // SAMPLE_MQTT_OVER_WEBSOCKETS
#ifdef SAMPLE_AMQP
    #include "iothubtransportamqp.h"
#endif // SAMPLE_AMQP
#ifdef SAMPLE_AMQP_OVER_WEBSOCKETS
    #include "iothubtransportamqp_websockets.h"
#endif // SAMPLE_AMQP_OVER_WEBSOCKETS
#ifdef SAMPLE_HTTP
    #include "iothubtransporthttp.h"
#endif // SAMPLE_HTTP

/* Paste in the your iothub connection string  */
static const char* connectionString = "[Fill in connection string]";

static bool g_continueRunning;
#define DOWORK_LOOP_NUM     3

static void deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback)
{
    (void)userContextCallback;

    printf("Device Twin update received (state=%s, size=%zu): %s\r\n", 
        MU_ENUM_TO_STRING(DEVICE_TWIN_UPDATE_STATE, update_state), size, payLoad);
}

static void reportedStateCallback(int status_code, void* userContextCallback)
{
    (void)userContextCallback;
    printf("Device Twin reported properties update completed with result: %d\r\n", status_code);

    g_continueRunning = false;
}


void iothub_module_client_sample_device_twin_run(void)
{
    IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol;
    IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle;
    g_continueRunning = true;

    // Select the Protocol to use with the connection
#ifdef SAMPLE_MQTT
    protocol = MQTT_Protocol;
#endif // SAMPLE_MQTT
#ifdef SAMPLE_MQTT_OVER_WEBSOCKETS
    protocol = MQTT_WebSocket_Protocol;
#endif // SAMPLE_MQTT_OVER_WEBSOCKETS
#ifdef SAMPLE_AMQP
    protocol = AMQP_Protocol;
#endif // SAMPLE_AMQP
#ifdef SAMPLE_AMQP_OVER_WEBSOCKETS
    protocol = AMQP_Protocol_over_WebSocketsTls;
#endif // SAMPLE_AMQP_OVER_WEBSOCKETS
#ifdef SAMPLE_HTTP
    protocol = HTTP_Protocol;
#endif // SAMPLE_HTTP

    if (platform_init() != 0)
    {
        (void)printf("Failed to initialize the platform.\r\n");
    }
    else
    {
        if ((iotHubModuleClientHandle = IoTHubModuleClient_LL_CreateFromConnectionString(connectionString, protocol)) == NULL)
        {
            (void)printf("ERROR: iotHubModuleClientHandle is NULL!\r\n");
        }
        else
        {
            bool traceOn = true;
            const char* reportedState = "{ 'device_property': 'new_value'}";
            size_t reportedStateSize = strlen(reportedState);

            (void)IoTHubModuleClient_LL_SetOption(iotHubModuleClientHandle, OPTION_LOG_TRACE, &traceOn);

            // Check the return of all API calls when developing your solution. Return checks omitted for sample simplification.

            (void)IoTHubModuleClient_LL_SetModuleTwinCallback(iotHubModuleClientHandle, deviceTwinCallback, iotHubModuleClientHandle);
            (void)IoTHubModuleClient_LL_SendReportedState(iotHubModuleClientHandle, (const unsigned char*)reportedState, reportedStateSize, reportedStateCallback, iotHubModuleClientHandle);

            do
            {
                IoTHubModuleClient_LL_DoWork(iotHubModuleClientHandle);
                ThreadAPI_Sleep(1);
            } while (g_continueRunning);

            for (size_t index = 0; index < DOWORK_LOOP_NUM; index++)
            {
                IoTHubModuleClient_LL_DoWork(iotHubModuleClientHandle);
                ThreadAPI_Sleep(1);
            }

            IoTHubModuleClient_LL_Destroy(iotHubModuleClientHandle);
        }
        platform_deinit();
    }
}

int main(void)
{
    iothub_module_client_sample_device_twin_run();
    return 0;
}

后续步骤Next steps

若要继续了解 IoT 中心入门知识并浏览其他 IoT 方案,请参阅:To continue getting started with IoT Hub and to explore other IoT scenarios, see: