Como converter um dispositivo existente para ser um dispositivo IoT Plug and Play

Este artigo descreve os passos que deve seguir para converter um dispositivo existente num dispositivo IoT Plug and Play. Descreve como criar o modelo que cada dispositivo IoT Plug and Play requer, e as alterações de código necessárias para permitir que o dispositivo funcione como um dispositivo IoT Plug and Play.

Para as amostras de código, este artigo mostra o código C que usa uma biblioteca MQTT para ligar a um hub IoT. Pode aplicar as alterações descritas neste artigo a dispositivos implementados com outras línguas e SDKs.

Para converter o seu dispositivo existente como um dispositivo IoT Plug and Play:

  1. Reveja o código do seu dispositivo para entender a telemetria, propriedades e comandos que implementa.
  2. Crie um modelo que descreva a telemetria, propriedades e comanda os seus dispositivos.
  3. Modifique o código do dispositivo para anunciar o ID do modelo quando este se ligar ao seu serviço.

Reveja o código do seu dispositivo

Antes de criar um modelo para o seu dispositivo, tem de compreender as capacidades existentes do seu dispositivo:

  • A telemetria que o dispositivo envia regularmente.
  • As propriedades apenas de leitura e writable do dispositivo sincronizam com o seu serviço.
  • Os comandos invocados do serviço a que o dispositivo responde.

Por exemplo, reveja os seguintes excertos de códigos de dispositivo que implementam várias capacidades do dispositivo. Estes exemplos baseiam-se na amostra no PnPMQTTWin32-Antes :

O seguinte corte mostra o dispositivo que envia telemetria de temperatura:

#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));
  }
}

O nome do campo de telemetria é temperature e o seu tipo é flutuante ou duplo. A definição de modelo para este tipo de telemetria parece o seguinte JSON. Para aprender o modo, consulte Design um modelo abaixo:

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

O seguinte corte mostra o dispositivo reportando um valor de propriedade:

#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));
  }
}

O nome da propriedade é maxTempSinceLastReboot e o seu tipo é flutuante ou duplo. Esta propriedade é reportada pelo dispositivo, o dispositivo nunca recebe uma atualização para este valor do serviço. A definição de modelo para esta propriedade parece o seguinte JSON. Para aprender o modo, consulte Design um modelo abaixo:

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

O seguinte corte mostra o dispositivo respondendo a mensagens do serviço:

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;
  }
}

O $iothub/methods/POST/getMaxMinReport/ tópico recebe um pedido de comando chamado getMaxMinReport do serviço, este pedido pode incluir uma carga útil com parâmetros de comando. O dispositivo envia uma resposta com uma carga útil que inclui maxTemp e minTemp valoriza.

O $iothub/twin/PATCH/properties/desired/ tópico recebe atualizações de propriedade do serviço. Este exemplo pressupõe que a atualização da propriedade é para o targetTemperature imóvel. Responde com um reconhecimento que parece {\"targetTemperature\":{\"value\":46,\"ac\":200,\"av\":12,\"ad\":\"success\"}}.

Em resumo, a amostra implementa as seguintes capacidades:

Name Tipo de capacidade Detalhes
temperatura Telemetria Assuma que o tipo de dados é o dobro
maxTempSinceLastReboot Propriedade Assuma que o tipo de dados é o dobro
targetTemperature Propriedade writable O tipo de dados é inteiro
obter Relatório DeMaxMin Comando Devoluções JSON com maxTemp e minTemp campos de tipo duplo

Desenhe um modelo

Cada dispositivo IoT Plug and Play tem um modelo que descreve as funcionalidades e capacidades do dispositivo. O modelo utiliza o Digital Twin Definition Language (DTDL) V2 para descrever as capacidades do dispositivo.

Para um modelo simples que mapeia as capacidades existentes do seu dispositivo, utilize os elementos DTDL de Telemetria, Propriedade e Comando .

Um modelo DTDL para a amostra descrita na secção anterior parece ser o seguinte exemplo:

{
  "@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"
            }
          ]
        }
      }
    }
  ]
}

Neste modelo:

  • O name mapa e schema valores para os dados que o dispositivo envia e recebe.
  • Todas as capacidades estão agrupadas numa única interface.
  • Os @type campos identificam os tipos DTDL, como propriedade e comando.
  • Campos como unit, displayNamee description fornecer informações adicionais para o serviço a utilizar. Por exemplo, a IoT Central utiliza estes valores quando exibe dados nos painéis de instrumentos do dispositivo.

Para saber mais, consulte ioT Plug and Play convenções e ioT Plug and Play guia de modelagem.

Atualizar o código

Se o seu dispositivo já estiver a trabalhar com Hub IoT ou IoT Central, não precisa de fazer alterações na implementação das suas capacidades de telemetria, propriedade e comando. Para que o dispositivo siga as convenções IoT Plug and Play, modifique a forma como o dispositivo se conecta ao seu serviço de modo a anunciar o ID do modelo que criou. O serviço pode então utilizar o modelo para compreender as capacidades do dispositivo. Por exemplo, o IoT Central pode utilizar o ID do modelo para recuperar automaticamente o modelo de um repositório e gerar um modelo de dispositivo para o seu dispositivo.

Os dispositivos IoT ligam-se ao seu serviço IoT através do Serviço de Provisionamento de Dispositivos (DPS) ou diretamente com uma cadeia de ligação.

Se o seu dispositivo utilizar DPS para ligar, inclua o ID do modelo na carga útil que envia quando registar o dispositivo. Para o modelo de exemplo mostrado anteriormente, a carga útil parece:

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

Para saber mais, consulte o Registo de Tempo de Execução - Dispositivo de Registo.

Se o seu dispositivo utilizar DPS para ligar ou ligar diretamente a uma cadeia de ligação, inclua o ID do modelo quando o seu código se ligar a Hub IoT. Por exemplo:

#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);

Passos seguintes

Agora que sabe converter um dispositivo existente para ser um dispositivo IoT Plug and Play, um próximo passo sugerido é ler o guia de modelagem IoT Plug and Play.