

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 开始使用终端设备 SDK
<a name="managedintegrations-sdk-device-onboarding"></a>

按照以下步骤在 Linux 设备上运行终端设备 SDK。本节将指导您完成环境设置、网络配置、硬件功能实现和端点配置。

**重要**  
`examples`目录中的演示应用程序及其中的平台抽象层 (PAL) 实现`platform/posix`仅供参考。请勿在生产环境中使用它们。

仔细查看以下步骤的每个步骤，确保设备与托管集成正确集成。

**集成终端设备 SDK**

1. <a name="managedintegrations-sdk-device-onboarding-ec2"></a>

**设置 Amazon EC2 实例**

   登录 AWS 管理控制台 并使用亚马逊 Linux AMI 启动亚马逊 EC2 实例。请参阅《[亚马逊*[弹性容器注册表用户指南》中的 Amazon](https://docs.aws.amazon.com/AmazonECR/latest/userguide/)* EC2 入门](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html)。

1. <a name="managedintegrations-sdk-device-onboarding-buildenv"></a>

**设置构建环境**

   在 2023/x86\_64 作为开发主机的 Amazon Linux 上构建代码。安装必要的编译依赖项：

   ```
   dnf install make gcc gcc-c++ cmake
   ```

1. <a name="managedintegrations-sdk-device-onboarding-network"></a>

**（可选）设置网络**

   终端设备 SDK 最好与物理硬件配合使用。如果使用 Amazon EC2，请不要执行此步骤。

   如果您在使用示例应用程序之前未使用 Amazon EC2，请初始化网络并将您的设备连接到可用 Wi-Fi 网络。在设备配置之前完成网络设置：

   ```
   /* Provisioning the device PKCS11 with claim credential. */
   status = deviceCredentialProvisioning();
   ```

1. <a name="managedintegrations-sdk-device-onboarding-buildenv"></a>

**配置配置参数**
**注意**  
在继续[操作之前，请按照 Provisionee](managedintegrations-sdk-device-provisionee.md) 获取索赔证书和私钥。

    `example/project_name/device_config.sh`使用以下配置参数修改配置文件：


**配置参数**  

<table>
<thead>
  <tr><th>宏观参数</th><th>说明</th><th>如何获取此信息</th></tr>
</thead>
<tbody>
  <tr><td><code>IOTMI_ROOT_CA_PATH</code></td><td>根 CA 证书文件。</td><td>您可以从<i>AWS IoT Core 开发者指南</i>的<a href="https://docs.aws.amazon.com/iot/latest/developerguide/iot-dc-prepare-device-test.html#iot-dc-prepare-device-test-step3">下载 Amazon 根 CA 证书</a>部分下载此文件。</td></tr>
  <tr><td><code>IOTMI_CLAIM_CERTIFICATE_PATH</code></td><td>索赔证书文件的路径。</td><td rowspan="2">要获取声明证书和私钥，请使用 <a href="https://docs.aws.amazon.com/iot-mi/latest/APIReference/API_CreateProvisioningProfile.html">CreateProvisioningProfile</a>API 创建配置文件。有关说明，请参阅<a href="managedintegrations-sdk-device-provisionee.md#sdk-provisionee-template-create">创建配置文件</a>。</td></tr>
  <tr><td><code>IOTMI_CLAIM_PRIVATE_KEY_PATH</code></td><td>声明私钥文件的路径。</td></tr>
  <tr><td><code>IOTMI_MANAGEDINTEGRATIONS_ENDPOINT</code></td><td>托管集成的终端节点 URL。</td><td>要获取托管集成端点，请使用 <a href="https://docs.aws.amazon.com/iot-mi/latest/APIReference/API_RegisterCustomEndpoint.html">RegisterCustomEndpoint</a>API。有关说明，请参阅<a href="managedintegrations-sdk-device-provisionee.md#sdk-provisionee-endpoint-create">注册自定义终端节点</a>。</td></tr>
  <tr><td>IOTMI 托管集成_端点_端口</td><td>托管集成端点的端口号</td><td>默认情况下，端口 8883 用于 MQTT 发布和订阅操作。端口 443 设置为设备使用的应用层协议协商 (ALPN) TLS 扩展。</td></tr>
</tbody>
</table>


1. <a name="managedintegrations-sdk-device-onboarding-builddemo"></a>

**构建并运行演示应用程序**

   本节演示了两个 Linux 演示应用程序：一个简单的安全摄像头和一个空气净化器，两者都使用 CMake 作为构建系统。

   1. 简单的安全摄像头应用程序

      要生成并运行应用程序，请执行以下命令：

      ```
      >cd <path-to-code-drop>
      # If you didn't generate cluster code earlier
      >(cd codegen && poetry run poetry install --no-root && ./gen-data-model-api.sh)
      >mkdir build
      >cd build
      >cmake ..
      >cmake —build .
      >./examples/iotmi_device_sample_camera/iotmi_device_sample_camera
      ```

      此演示 C-Functions 为具有 RTC 会话控制器和录制集群的模拟摄像机实现了低级。在运行[置备人工作流程](managedintegrations-sdk-device-provisionee.md#managedintegrations-sdk-device-provisionee-how)之前，请完成中提到的流程。

      演示应用程序的输出示例：

      ```
      [2406832727][MAIN][INFO] ======= Device initialization and WIFI provisioning =======
      [2406832728][MAIN][INFO] fleetProvisioningTemplateName: XXXXXXXXXXX
      [2406832728][MAIN][INFO] managedintegrationsEndpoint: XXXXXXXXX.{{account-prefix}}-ats.iot.{{region}}.amazonaws.com
      [2406832728][MAIN][INFO] pDeviceSerialNumber: XXXXXXXXXXXX
      [2406832728][MAIN][INFO] universalProductCode: XXXXXXXXXXXX
      [2406832728][MAIN][INFO] rootCertificatePath: XXXXXXXXX
      [2406832728][MAIN][INFO] pClaimCertificatePath: XXXXXXXX
      [2406832728][MAIN][INFO] pClaimKeyPath: XXXXXXXXXXXXXXXXX
      [2406832728][MAIN][INFO] deviceInfo.serialNumber XXXXXXXXXXXX
      [2406832728][MAIN][INFO] deviceInfo.universalProductCode XXXXXXXXXXXXXXX
      [2406832728][PKCS11][INFO] PKCS #11 successfully initialized.
      [2406832728][MAIN][INFO] ============= Start certificate provisioning =============
      [2406832728][PKCS11][INFO] ======== Loading Root CA and claim credentials through PKCS#11 interface ========
      [2406832728][PKCS11][INFO] Writing certificate into label "Root Cert".
      [2406832728][PKCS11][INFO] Creating a 0x1 type object.
      [2406832728][PKCS11][INFO] Writing certificate into label "Claim Cert".
      [2406832728][PKCS11][INFO] Creating a 0x1 type object.
      [2406832728][PKCS11][INFO] Creating a 0x3 type object.
      [2406832728][MAIN][INFO] ======== Fleet-provisioning-by-Claim ========
      [2025-01-02 01:43:11.404995144][iotmi_device_sdkLog][INFO] [2406832728][MQTT_AGENT][INFO]
      [2025-01-02 01:43:11.405106991][iotmi_device_sdkLog][INFO] Establishing a TLS session to XXXXXXXXXXXXXXX.{{account-prefix}}-ats.iot.{{region}}.amazonaws.com
      [2025-01-02 01:43:11.405119166][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:11.844812513][iotmi_device_sdkLog][INFO] [2406833168][MQTT_AGENT][INFO]
      [2025-01-02 01:43:11.844842576][iotmi_device_sdkLog][INFO] TLS session connected
      [2025-01-02 01:43:11.844852105][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:12.296421687][iotmi_device_sdkLog][INFO] [2406833620][MQTT_AGENT][INFO]
      [2025-01-02 01:43:12.296449663][iotmi_device_sdkLog][INFO] Session present: 0.
      [2025-01-02 01:43:12.296458997][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:12.296467793][iotmi_device_sdkLog][INFO] [2406833620][MQTT_AGENT][INFO]
      [2025-01-02 01:43:12.296476275][iotmi_device_sdkLog][INFO] MQTT connect with clean session.
      [2025-01-02 01:43:12.296484350][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:13.171056119][iotmi_device_sdkLog][INFO] [2406834494][FLEET_PROVISIONING][INFO]
      [2025-01-02 01:43:13.171082442][iotmi_device_sdkLog][INFO] Received accepted response from Fleet Provisioning CreateKeysAndCertificate API.
      [2025-01-02 01:43:13.171092740][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:13.171122834][iotmi_device_sdkLog][INFO] [2406834494][FLEET_PROVISIONING][INFO]
      [2025-01-02 01:43:13.171132400][iotmi_device_sdkLog][INFO] Received privatekey and certificate with Id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      [2025-01-02 01:43:13.171141107][iotmi_device_sdkLog][INFO]
      [2406834494][PKCS11][INFO] Creating a 0x3 type object.
      [2406834494][PKCS11][INFO] Writing certificate into label "Device Cert".
      [2406834494][PKCS11][INFO] Creating a 0x1 type object.
      [2025-01-02 01:43:18.584615126][iotmi_device_sdkLog][INFO] [2406839908][FLEET_PROVISIONING][INFO]
      [2025-01-02 01:43:18.584662031][iotmi_device_sdkLog][INFO] Received accepted response from Fleet Provisioning RegisterThing API.
      [2025-01-02 01:43:18.584671912][iotmi_device_sdkLog][INFO]
      [2025-01-02 01:43:19.100030237][iotmi_device_sdkLog][INFO] [2406840423][FLEET_PROVISIONING][INFO]
      [2025-01-02 01:43:19.100061720][iotmi_device_sdkLog][INFO] Fleet-provisioning iteration 1 is successful.
      [2025-01-02 01:43:19.100072401][iotmi_device_sdkLog][INFO]
      [2406840423][MQTT][ERROR] MQTT Connection Disconnected Successfully
      [2025-01-02 01:43:19.216938181][iotmi_device_sdkLog][INFO] [2406840540][MQTT_AGENT][INFO]
      [2025-01-02 01:43:19.216963713][iotmi_device_sdkLog][INFO] MQTT agent thread leaves thread loop for iotmiDev_MQTTAgentStop.
      [2025-01-02 01:43:19.216973740][iotmi_device_sdkLog][INFO]
      [2406840540][MAIN][INFO] iotmiDev_MQTTAgentStop is called to break thread loop function.
      [2406840540][MAIN][INFO] Successfully provision the device.
      [2406840540][MAIN][INFO] Client ID : XXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXX
      [2406840540][MAIN][INFO] Managed thing ID : XXXXXXXXXXXXXXXXXXXXXXX
      [2406840540][MAIN][INFO] ======================== application loop =================
      [2025-01-02 01:43:19.217094828][iotmi_device_sdkLog][INFO] [2406840540][MQTT_AGENT][INFO]
      [2025-01-02 01:43:19.217124600][iotmi_device_sdkLog][INFO] Establishing a TLS session to XXXXXXXXX.{{account-prefix}}-ats.iot.{{region}}.amazonaws.com:8883
      [2025-01-02 01:43:19.217138724][iotmi_device_sdkLog][INFO]
      [2406840540][Cluster OnOff][INFO] exampleOnOffInitCluster() for endpoint#1
      [2406840540][MAIN][INFO] Press Ctrl+C when you finish testing...
      [2406840540][Cluster ActivatedCarbonFilterMonitoring][INFO] exampleActivatedCarbonFilterMonitoringInitCluster() for endpoint#1
      [2406840540][Cluster AirQuality][INFO] exampleAirQualityInitCluster() for endpoint#1
      [2406840540][Cluster CarbonDioxideConcentrationMeasurement][INFO] exampleCarbonDioxideConcentrationMeasurementInitCluster() for endpoint#1
      [2406840540][Cluster FanControl][INFO] exampleFanControlInitCluster() for endpoint#1
      [2406840540][Cluster HepaFilterMonitoring][INFO] exampleHepaFilterMonitoringInitCluster() for endpoint#1
      [2406840540][Cluster Pm1ConcentrationMeasurement][INFO] examplePm1ConcentrationMeasurementInitCluster() for endpoint#1
      [2406840540][Cluster Pm25ConcentrationMeasurement][INFO] examplePm25ConcentrationMeasurementInitCluster() for endpoint#1
      [2406840540][Cluster TotalVolatileOrganicCompoundsConcentrationMeasurement][INFO] exampleTotalVolatileOrganicCompoundsConcentrationMeasurementInitCluster() for endpoint#1
      [2025-01-02 01:43:19.648185488][iotmi_device_sdkLog][INFO] [2406840971][MQTT_AGENT][INFO]
      [2025-01-02 01:43:19.648211988][iotmi_device_sdkLog][INFO] TLS session connected
      [2025-01-02 01:43:19.648225583][iotmi_device_sdkLog][INFO]
      
      [2025-01-02 01:43:19.938281231][iotmi_device_sdkLog][INFO] [2406841261][MQTT_AGENT][INFO]
      [2025-01-02 01:43:19.938304799][iotmi_device_sdkLog][INFO] Session present: 0.
      [2025-01-02 01:43:19.938317404][iotmi_device_sdkLog][INFO]
      ```

   1. 简单的空气净化器应用

      要生成并运行应用程序，请运行以下命令：

      ```
      >cd <path-to-code-drop>
      # If you didn't generate cluster code earlier
      >(cd codegen && poetry run poetry install --no-root && ./gen-data-model-api.sh)
      >mkdir build
      >cd build
      >cmake ..
      >cmake --build .
      >./examples/iotmi_device_dm_air_purifier/iotmi_device_dm_air_purifier_demo
      ```

      此演示 C-Functions 为具有两个端点和以下支持的集群的模拟空气净化器实现了低级：


**空气净化器端点支持的集群**  

<table>
<thead>
  <tr><th>端点</th><th>集群</th></tr>
</thead>
<tbody>
  <tr><td rowspan="4">终点 #1: 空气净化器</td><td>OnOff</td></tr>
  <tr><td>风扇控制</td></tr>
  <tr><td>HEPA 过滤器监测</td></tr>
  <tr><td>活性炭过滤器监测</td></tr>
  <tr><td rowspan="6">终点 #2: 空气质量传感器</td><td>空气质量</td></tr>
  <tr><td>二氧化碳浓度测量</td></tr>
  <tr><td>甲醛浓度测量</td></tr>
  <tr><td>Pm25 浓度测量 </td></tr>
  <tr><td>Pm1 浓度测量</td></tr>
  <tr><td>总挥发性有机化合物浓度测量</td></tr>
</tbody>
</table>


      输出与相机演示应用程序类似，但支持的集群不同。

1. <a name="managedintegrations-sdk-device-onboarding-next"></a>

**后续步骤：**

    托管集成终端设备软件开发工具包和演示应用程序现已在您的 Amazon EC2 实例上运行。这使您能够在自己的物理硬件上开发和测试应用程序。通过此设置，您可以利用托管集成服务来控制您的 AWS IoT 设备。

   1. <a name="managedintegrations-sdk-device-onboarding-hardwarecallback"></a>

**开发硬件回调函数**

      在实现硬件回调函数之前，请先了解 API 的工作原理。此示例使用 On/Off 群集和OnOff属性来控制设备功能。有关 API 的详细信息，请参阅[低级 C-Function API](managedintegrations-sdk-device-api.md)。

      ```
      struct DeviceState
      {
        struct iotmiDev_Agent *agent;
        struct iotmiDev_Endpoint *endpointLight;
        /* This simulates the HW state of OnOff */
        bool hwState;   
      };
      
      /* This implementation for OnOff getter just reads
         the state from the DeviceState */
      iotmiDev_DMStatus exampleGetOnOff(bool *value, void *user)
      {
         struct DeviceState *state = (struct DeviceState *)(user);
        *value = state->hwState;
        return iotmiDev_DMStatusOk;
      }
      ```

   1. <a name="managedintegrations-sdk-device-onboarding-endpoints"></a>

**设置端点并挂接硬件回调函数**

      实现函数后，创建端点并注册您的回调。完成以下任务：

      1. 创建设备代理。

         1. 在调用任何其他 SDK 函数`iotmiDev_Agent_new()`之前，使用创建设备代理。

         1.  **您的配置必须至少包含 t **hingID 和 clientID** 参数。**

         1.  使用该`iotmiDev_Agent_initDefaultConfig()`函数为队列大小和最大端点等参数设置合理的默认值。

         1. 使用完资源后，使用该`iotmiDev_Agent_free()`函数将其释放。这样可以防止内存泄漏并确保在应用程序中进行适当的资源管理。

      1. 为要支持的每个集群结构填充回调函数指针。

      1. 设置终端节点并注册支持的集群。

         使用创建终端节点`iotmiDev_Agent_addEndpoint()`，这需要：

         1. 唯一的终端节点 ID。

         1. 描述性端点名称

         1. 一种或多种符合 AWS 数据模型定义的设备类型。

         1. 创建终端节点后，使用相应的集群特定注册功能注册集群。

         1. 每个集群注册都需要使用属性和命令的回调函数。系统会将您的用户上下文指针传递给回调，以在两次调用之间保持状态。

      ```
      struct DeviceState
      {
          struct iotmiDev_Agent * agent;
          struct iotmiDev_Endpoint *endpoint1;
      
          /* OnOff cluster states*/
          bool hwState;
      };
      
      
      /* This implementation for OnOff getter just reads
         the state from the DeviceState */
      iotmiDev_DMStatus exampleGetOnOff( bool * value, void * user )
      {
          struct DeviceState * state = ( struct DeviceState * ) ( user );
          *value = state->hwState;
          printf( "%s(): state->hwState: %d\n", __func__, state->hwState );
          return iotmiDev_DMStatusOk;
      }
      
      iotmiDev_DMStatus exampleGetOnTime( uint16_t * value, void * user )
      {
          *value = 0;
          printf( "%s(): OnTime is %u\n", __func__, *value );
          return iotmiDev_DMStatusOk;
      }
      
      iotmiDev_DMStatus exampleGetStartUpOnOff( iotmiDev_OnOff_StartUpOnOffEnum * value, void * user )
      {
          *value = iotmiDev_OnOff_StartUpOnOffEnum_Off;
          printf( "%s(): StartUpOnOff is %d\n", __func__, *value );
          return iotmiDev_DMStatusOk;
      }
      
      void setupOnOff( struct DeviceState *state )
      {
          struct iotmiDev_clusterOnOff clusterOnOff = {
              .getOnOff = exampleGetOnOff,
              .getOnTime = exampleGetOnTime,
              .getStartUpOnOff = exampleGetStartUpOnOff,
          };
          iotmiDev_OnOffRegisterCluster( state->endpoint1,
                                          &clusterOnOff,
                                          ( void * ) state);
      }
      
      
      /* Here is the sample setting up an endpoint 1 with OnOff
         cluster. Note all error handling code is omitted. */
      void setupAgent(struct DeviceState *state)
      {
          struct iotmiDev_Agent_Config config = {
              .thingId = IOTMI_DEVICE_MANAGED_THING_ID,
              .clientId = IOTMI_DEVICE_CLIENT_ID,
          };
          iotmiDev_Agent_InitDefaultConfig(&config);
      
          /* Create a device agent before calling other SDK APIs */
          state->agent = iotmiDev_Agent_new(&config);
          
          /* Create endpoint#1 */
          state->endpoint1 = iotmiDev_Agent_addEndpoint( state->agent,
                                                          1,
                                                          "Data Model Handler Test Device",
                                                          (const char*[]){ "Camera" },
                                                          1 );
          setupOnOff(state);
      }
      ```

   1. <a name="managedintegrations-sdk-device-onboarding-jobhandler"></a>

**使用作业处理程序获取作业文档**

      1. 发起对您的 OTA 应用程序的调用：

         ```
         static iotmi_JobCurrentStatus_t processOTA( iotmi_JobData_t * pJobData )
         {
             iotmi_JobCurrentStatus_t jobCurrentStatus = JobSucceeded;
         
         ...
             // This function should create OTA tasks
             jobCurrentStatus = YOUR_OTA_FUNCTION(iotmi_JobData_t * pJobData);
         ...
         
             return jobCurrentStatus;
         }
         ```

      1. 调用`iotmi_JobsHandler_start`以初始化作业处理程序。

      1. `iotmi_JobsHandler_getJobDocument`致电从托管集成中检索任务文档。

      1. 成功获取任务文档后，在`processOTA`函数中写入您的自定义 OTA 操作并返回`JobSucceeded`状态。

         ```
         static void prvJobsHandlerThread( void * pParam )
         {
             JobsHandlerStatus_t status = JobsHandlerSuccess;
             iotmi_JobData_t jobDocument;
             iotmiDev_DeviceRecord_t * pThreadParams = ( iotmiDev_DeviceRecord_t * ) pParam;
             iotmi_JobsHandler_config_t config = { .pManagedThingID = pThreadParams->pManagedThingID, .jobsQueueSize = 10 };
         
             status = iotmi_JobsHandler_start( &config );
         
             if( status != JobsHandlerSuccess )
             {
                 LogError( ( "Failed to start Jobs Handler." ) );
                 return;
             }
         
             while( !bExit )
             {
                 status = iotmi_JobsHandler_getJobDocument( &jobDocument, 30000 );
         
                 switch( status )
                 {
                     case JobsHandlerSuccess:
                     {
                         LogInfo( ( "Job document received." ) );
                         LogInfo( ( "Job ID: %.*s", ( int ) jobDocument.jobIdLength, jobDocument.pJobId ) );
                         LogInfo( ( "Job document: %.*s", ( int ) jobDocument.jobDocumentLength, jobDocument.pJobDocument ) );
         
                         /* Process the job document */
                         iotmi_JobCurrentStatus_t jobStatus = processOTA( &jobDocument );
         
                         iotmi_JobsHandler_updateJobStatus( jobDocument.pJobId, jobDocument.jobIdLength, jobStatus, NULL, 0 );
         
                         iotmiJobsHandler_destroyJobDocument(&jobDocument);
         
                         break;
                     }
                     case JobsHandlerTimeout:
                     {
                         LogInfo( ( "No job document available. Polling for job document." ) );
         
                         iotmi_JobsHandler_pollJobDocument();
         
                         break;
                     }
                     default:
                     {
                         LogError( ( "Failed to get job document." ) );
                         break;
                     }
                 }
             }
         
             while( iotmi_JobsHandler_getJobDocument( &jobDocument, 0 ) == JobsHandlerSuccess )
             {
                 /* Before stopping the Jobs Handler, process all the remaining jobs. */
         
                 LogInfo( ( "Job document received before stopping." ) );
                 LogInfo( ( "Job ID: %.*s", ( int ) jobDocument.jobIdLength, jobDocument.pJobId ) );
                 LogInfo( ( "Job document: %.*s", ( int ) jobDocument.jobDocumentLength, jobDocument.pJobDocument ) );
         
                 storeJobs( &jobDocument );
         
                 iotmiJobsHandler_destroyJobDocument(&jobDocument);
             }
         
             iotmi_JobsHandler_stop();
         
             LogInfo( ( "Job handler thread end." ) );
         
         }
         ```