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

Este artigo descreve as etapas que você deve seguir para converter um dispositivo existente em um dispositivo IoT Plug and Play. Ele 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 os exemplos de código, este artigo mostra o código C que usa uma biblioteca MQTT para se conectar a um hub IoT. Você pode aplicar as alterações descritas neste artigo a dispositivos implementados com outros idiomas e SDKs.

Para converter seu dispositivo existente em um dispositivo IoT Plug and Play:

  1. Revise o código do dispositivo para entender a telemetria, as propriedades e os comandos implementados.
  2. Crie um modelo que descreva a telemetria, as propriedades e os comandos implementados pelo dispositivo.
  3. Modifique o código do dispositivo para anunciar o ID do modelo quando ele se conectar ao seu serviço.

Rever o código do dispositivo

Antes de criar um modelo para o seu dispositivo, você precisa entender os recursos existentes do seu dispositivo:

  • A telemetria que o dispositivo envia regularmente.
  • As propriedades somente leitura e graváveis que o dispositivo sincroniza com seu serviço.
  • Os comandos invocados a partir do serviço ao qual o dispositivo responde.

Por exemplo, revise os seguintes trechos de código de dispositivo que implementam vários recursos de dispositivo.

O trecho a seguir mostra a telemetria de temperatura de envio do dispositivo:

#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 é e seu tipo é temperature float ou double. A definição de modelo para esse tipo de telemetria se parece com o JSON a seguir. Para aprender o modo, consulte Criar um modelo abaixo:

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

O trecho a seguir mostra o dispositivo relatando 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 é e seu tipo é maxTempSinceLastReboot float ou double. Esta propriedade é relatada pelo dispositivo, o dispositivo nunca recebe uma atualização para esse valor do serviço. A definição de modelo para esta propriedade se parece com o seguinte JSON. Para aprender o modo, consulte Criar 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 trecho a seguir 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 uma solicitação de comando chamada getMaxMinReport do serviço, essa solicitação pode incluir uma carga útil com parâmetros de comando. O dispositivo envia uma resposta com uma carga que inclui maxTemp e minTemp valores.

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

Em resumo, o exemplo implementa os seguintes recursos:

Nome Capability type Detalhes
temperatura Telemetria Suponha que o tipo de dados é duplo
maxTempSinceLastReboot Property Suponha que o tipo de dados é duplo
targetTemperature Propriedade gravável O tipo de dados é inteiro
getMaxMinReport Comando Retorna JSON com maxTemp e minTemp campos do tipo double

Projetar um modelo

Cada dispositivo IoT Plug and Play tem um modelo que descreve os recursos e capacidades do dispositivo. O modelo usa a Digital Twin Definition Language (DTDL) para descrever os recursos do dispositivo.

Para um modelo simples que mapeia os recursos existentes do seu dispositivo, use os elementos DTDL Telemetria, Propriedade e Comando .

Um modelo DTDL para o exemplo descrito na seção anterior se parece com o exemplo a seguir:

{
  "@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:

  • Os name valores e são mapeados para os dados que o dispositivo envia e schema recebe.
  • Todos os recursos são agrupados em uma única interface.
  • Os @type campos identificam os tipos DTDL, como Property e Command.
  • Campos como unit, displayNamee description fornecem informações adicionais para o serviço usar. Por exemplo, o IoT Central usa esses valores quando exibe dados em painéis de dispositivos.

Para saber mais, consulte Convenções IoT Plug and Play e Guia de modelagem IoT Plug and Play.

Atualizar o código

Se o seu dispositivo já estiver trabalhando com o Hub IoT ou o IoT Central, não será necessário fazer alterações na implementação de seus recursos de telemetria, propriedade e comando. Para fazer com que o dispositivo siga as convenções IoT Plug and Play, modifique a maneira como o dispositivo se conecta ao seu serviço para que ele anuncie a ID do modelo que você criou. O serviço pode usar o modelo para entender os recursos do dispositivo. Por exemplo, o IoT Central pode usar 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 se conectam ao seu serviço IoT por meio do Serviço de Provisionamento de Dispositivo (DPS) ou diretamente com uma cadeia de conexão.

Se o seu dispositivo utilizar DPS para estabelecer ligação, inclua o ID do modelo na carga que enviar quando registar o dispositivo. Para o modelo de exemplo mostrado anteriormente, a carga útil tem a seguinte aparência:

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

Para saber mais, consulte Registro de tempo de execução - Registrar dispositivo.

Se o dispositivo usa DPS para se conectar ou se conectar diretamente com uma cadeia de conexão, inclua o ID do modelo quando o código se conectar ao 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);

Próximos passos

Agora que você sabe como converter um dispositivo existente para ser um dispositivo IoT Plug and Play, uma próxima etapa sugerida é ler o guia de modelagem IoT Plug and Play.