共用方式為


如何將現有裝置轉換成 IoT 隨插即用裝置

本文概述將現有裝置轉換成 IoT 隨插即用裝置時所應遵循的步驟。 其描述如何建立每個 IoT 隨插即用裝置所需的模型,以及讓裝置能夠做為 IoT 隨插即用裝置運作的必要程式碼變更。

針對程式碼範例,本文示範使用 MQTT 程式庫連線到 IoT 中樞的 C 程式碼。 您可以將本文中所述的變更套用至使用其他語言和 SDK 實作的裝置。

若要將現有裝置轉換成 IoT 隨插即用裝置:

  1. 檢閱您的裝置程式碼,以了解其實作的遙測、屬性和命令。
  2. 建立可描述裝置所實作遙測、屬性和命令的模型。
  3. 修改裝置程式碼,以在其連線到您的服務時宣告模型識別碼。

檢閱您的裝置程式碼

建立裝置的模型之前,您必須了解裝置的現有功能:

  • 裝置會定期傳送的遙測。
  • 裝置與服務同步處理的唯讀及可寫入屬性。
  • 從裝置回應的目標服務叫用的命令。

例如,檢閱下列實作各種裝置功能的裝置程式碼片段。

下列程式碼片段示範傳送溫度遙測的裝置:

#define TOPIC "devices/" DEVICEID "/messages/events/"

// ...

void Thermostat_SendCurrentTemperature()
{
  char msg[] = "{\"temperature\":25.6}";
  int msgId = rand();
  int rc = mosquitto_publish(mosq, &msgId, TOPIC, sizeof(msg) - 1, msg, 1, true);
  if (rc != MOSQ_ERR_SUCCESS)
  {
    printf("Error: %s\r\n", mosquitto_strerror(rc));
  }
}

遙測欄位的名稱為 temperature,其類型為 float 或 double。 此遙測類型的模型定義看似下列 JSON。 若要了解模式,請參閱下列設計模型

{
  "@type": [
    "Telemetry"
  ],
  "name": "temperature",
  "displayName": "Temperature",
  "description": "Temperature in degrees Celsius.",
  "schema": "double"
}

下列程式碼片段示範回報屬性值的裝置:

#define DEVICETWIN_MESSAGE_PATCH "$iothub/twin/PATCH/properties/reported/?$rid=patch_temp"

static void SendMaxTemperatureSinceReboot()
{
  char msg[] = "{\"maxTempSinceLastReboot\": 42.500}";
  int msgId = rand();
  int rc = mosquitto_publish(mosq, &msgId, DEVICETWIN_MESSAGE_PATCH, sizeof(msg) - 1, msg, 1, true);
  if (rc != MOSQ_ERR_SUCCESS)
  {
    printf("Error: %s\r\n", mosquitto_strerror(rc));
  }
}

屬性的名稱為 maxTempSinceLastReboot,其類型為 float 或 double。 裝置會報告這個屬性,裝置一律不會從服務收到此值的更新。 此屬性的模型定義看似下列 JSON。 若要了解模式,請參閱下列設計模型

{
  "@type": [
    "Property"
  ],
  "name": "maxTempSinceLastReboot",
  "schema": "double",
  "displayName": "Max temperature since last reboot.",
  "description": "Returns the max temperature since last device reboot."
}

下列程式碼片段示範回應服務所發出訊息的裝置:

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
  printf("Message received: %s payload: %s \r\n", message->topic, (char*)message->payload);
  
  if (strncmp(message->topic, "$iothub/methods/POST/getMaxMinReport/?$rid=1",37) == 0)
  {
    char* pch;
    char* context;
    int msgId = 0;
    pch = strtok_s((char*)message->topic, "=",&context);
    while (pch != NULL)
    {
      pch = strtok_s(NULL, "=", &context);
      if (pch != NULL) {
        char * pEnd;
        msgId = strtol(pch,&pEnd,16 );
      }
    }
    char topic[64];
    sprintf_s(topic, "$iothub/methods/res/200/?$rid=%d", msgId);
    char msg[] = "{\"maxTemp\":83.51,\"minTemp\":77.68}";
    int rc = mosquitto_publish(mosq, &msgId, topic, sizeof(msg) - 1, msg, 1, true);
    if (rc != MOSQ_ERR_SUCCESS)
    {
      printf("Error: %s\r\n", mosquitto_strerror(rc));
    }
    delete pch;
  }

  if (strncmp(message->topic, "$iothub/twin/PATCH/properties/desired/?$version=1", 38) == 0)
  {
    char* pch;
    char* context;
    int version = 0; 
    pch = strtok_s((char*)message->topic, "=", &context);
    while (pch != NULL)
    {
      pch = strtok_s(NULL, "=", &context);
      if (pch != NULL) {
        char* pEnd;
        version = strtol(pch, &pEnd, 10);
      }
    }
    // To do: Parse payload and extract target value
    char msg[128];
    int value = 46;
    sprintf_s(msg, "{\"targetTemperature\":{\"value\":%d,\"ac\":200,\"av\":%d,\"ad\":\"success\"}}", value, version);
    int rc = mosquitto_publish(mosq, &version, DEVICETWIN_MESSAGE_PATCH, strlen(msg), msg, 1, true);
    if (rc != MOSQ_ERR_SUCCESS)
    {
      printf("Error: %s\r\n", mosquitto_strerror(rc));
    }
    delete pch;
  }
}

$iothub/methods/POST/getMaxMinReport/ 主題會接收服務所發出呼叫 getMaxMinReport 的命令要求,此要求可能包含具有命令參數的承載。 裝置會傳送包含 maxTempminTemp 值的承載回應。

$iothub/twin/PATCH/properties/desired/ 主題會接收服務發出的屬性更新。 本範例假設屬性更新適用於 targetTemperature 屬性。 其會利用看起來像 {\"targetTemperature\":{\"value\":46,\"ac\":200,\"av\":12,\"ad\":\"success\"}} 的通知回應。

總而言之,此範例會實作下列功能:

名稱 功能類型 詳細資料
溫度 遙測 假設資料類型為 double
maxTempSinceLastReboot 屬性 假設資料類型為 double
targetTemperature 可寫入的屬性 資料類型為 Integer
getMaxMinReport Command 傳回 JSON,其類型為 double 的 maxTempminTemp 欄位

設計模型

每個 IoT 隨插即用裝置都有一個模型,會描述裝置的特性和功能。 此模型會使用數位對應項定義語言 (DTDL) 來描述裝置功能。

針對對應裝置現有功能的簡單模型,請使用 TelemetryPropertyCommand DTDL 元素。

上一節所述範例的 DTDL 模型看似下列範例:

{
  "@context": "dtmi:dtdl:context;2",
  "@id": "dtmi:com:example:ConvertSample;1",
  "@type": "Interface",
  "displayName": "Simple device",
  "description": "Example that shows model for simple device converted to act as an IoT Plug and Play device.",
  "contents": [
    {
      "@type": [
        "Telemetry",
        "Temperature"
      ],
      "name": "temperature",
      "displayName": "Temperature",
      "description": "Temperature in degrees Celsius.",
      "schema": "double",
      "unit": "degreeCelsius"
    },
    {
      "@type": [
        "Property",
        "Temperature"
      ],
      "name": "targetTemperature",
      "schema": "double",
      "displayName": "Target Temperature",
      "description": "Allows to remotely specify the desired target temperature.",
      "unit": "degreeCelsius",
      "writable": true
    },
    {
      "@type": [
        "Property",
        "Temperature"
      ],
      "name": "maxTempSinceLastReboot",
      "schema": "double",
      "unit": "degreeCelsius",
      "displayName": "Max temperature since last reboot.",
      "description": "Returns the max temperature since last device reboot."
    },
    {
      "@type": "Command",
      "name": "getMaxMinReport",
      "displayName": "Get Max-Min report.",
      "description": "This command returns the max and min temperature.",
      "request": {
      },
      "response": {
        "name": "tempReport",
        "displayName": "Temperature Report",
        "schema": {
          "@type": "Object",
          "fields": [
            {
              "name": "maxTemp",
              "displayName": "Max temperature",
              "schema": "double"
            },
            {
              "name": "minTemp",
              "displayName": "Min temperature",
              "schema": "double"
            }
          ]
        }
      }
    }
  ]
}

在此模型中:

  • nameschema 值會對應至裝置傳送和接收的資料。
  • 所有功能都會分組在單一介面中。
  • @type 欄位會識別 DTDL 類型,例如 PropertyCommand
  • unitdisplayNamedescription 等欄位會提供服務要使用的額外資訊。 例如,IoT Central 會在裝置儀表板上顯示資料時使用這些值。

若要深入了解,請參閱 IoT 隨插即用慣例IoT 隨插即用模型化指南

更新程式碼

如果您的裝置已經使用 IoT 中樞或 IoT Central,您就不需要對其遙測、屬性和命令功能的實作進行任何變更。 若要使裝置遵循 IoT 隨插即用慣例,請修改裝置連線到服務的方式,以便其宣告您所建立模型的識別碼。 接著,服務可以使用模型來了解裝置功能。 例如,IoT Central 可以使用模型識別碼,從存放庫自動擷取模型,並產生您裝置適用的裝置範本。

IoT 裝置會透過裝置佈建服務 (DPS) 或是直接使用連接字串來連線到 IoT 服務。

如果您的裝置使用 DPS 進行連線,請在您註冊裝置時傳送的承載中包含模型識別碼。 針對先前顯示的範例模型,承載看似如此:

{
  "modelId" : "dtmi:com:example:ConvertSample;1"
}

若要深入了解,請參閱執行階段註冊 - 註冊裝置

如果您的裝置使用 DPS 進行連線,或利用連接字串直接連線,請在程式碼連線到 IoT 中樞時,包含模型識別碼。 例如:

#define USERNAME IOTHUBNAME ".azure-devices.net/" DEVICEID "/?api-version=2020-09-30&model-id=dtmi:com:example:ConvertSample;1"

// ...

mosquitto_username_pw_set(mosq, USERNAME, PWD);

// ...

rc = mosquitto_connect(mosq, HOST, PORT, 10);

下一步

既然您已了解如何將現有的裝置轉換成 IoT 隨插即用裝置,現在建議的下一個步驟是閱讀 IoT 隨插即用模型化指南