Conectar el dispositivo a la solución preconfigurada de supervisión remota (Windows)

Información general de escenario

En este escenario, creará un dispositivo que envía la siguiente telemetría a la solución preconfigurada de supervisión remota:

  • Temperatura exterior
  • Temperatura interior
  • Humedad

Para simplificar, el código del dispositivo genera valores de ejemplo, pero le recomendamos que amplíe el ejemplo conectando sensores reales a su dispositivo y enviando telemetría real.

El dispositivo también puede responder a los métodos que se invocan desde el panel de la solución y los valores de propiedades deseadas establecidos en el panel de la solución.

Para completar este tutorial, deberá tener una cuenta activa de Azure. En caso de no tener cuenta, puede crear una de evaluación gratuita en tan solo unos minutos. Para obtener más información, consulte Evaluación gratuita de Azure.

Antes de comenzar

Antes de escribir ningún código para el dispositivo, debe aprovisionar la solución preconfigurada de supervisión remota y aprovisionar un nuevo dispositivo personalizado en esa solución.

Aprovisionar su solución preconfigurada de supervisión remota

El dispositivo que cree en este tutorial enviará datos a una instancia de la solución preconfigurada de supervisión remota. Si todavía no aprovisionó la solución preconfigurada de supervisión remota en su cuenta de Azure, use estos pasos:

  1. En la página https://www.azureiotsolutions.com/, haga clic en + para crear una solución.
  2. Haga clic en Seleccionar en el panel de supervisión remota para crear la solución.
  3. En la página Create Remote monitoring solution (Crear solución de supervisión remota), escriba el nombre de solución que prefiera, seleccione la región en la que desea realizar la implementación y seleccione la suscripción de Azure que desea usar. Haga clic en Crear solución.
  4. Espere a que finalice el proceso de aprovisionamiento.

Advertencia

Las soluciones preconfiguradas utilizan servicios de Azure facturables. Para evitar gastos innecesarios, asegúrese de quitar la solución preconfigurada de la suscripción cuando haya terminado. Para quitar completamente una solución preconfigurada de su suscripción, diríjase a la página https://www.azureiotsolutions.com/.

Cuando finalice el proceso de aprovisionamiento para la solución de supervisión remota, haga clic en Iniciar para abrir el panel de la solución en el explorador.

Panel de soluciones

Aprovisionar el dispositivo en la solución de supervisión remota

Nota

Si ya ha aprovisionado un dispositivo en la solución, puede omitir este paso. Debe conocer las credenciales del dispositivo cuando cree la aplicación cliente.

Para que un dispositivo se conecte a la solución preconfigurada, debe identificarse en Azure IoT Hub con credenciales válidas. Puede recuperar las credenciales del dispositivo desde el panel de la solución. Incluirá las credenciales del dispositivo en la aplicación de cliente más adelante en este tutorial.

Para agregar un dispositivo a su solución de supervisión remota, complete los pasos siguientes en el panel de la solución:

  1. En la esquina inferior izquierda del panel, haga clic en Agregar un dispositivo.

    Agregar un dispositivo

  2. En el panel Dispositivo personalizado, haga clic en Agregar nuevo.

    Agregar un dispositivo personalizado

  3. Elija Permitirme definir mi propio id. de dispositivo. Especifique un id. de dispositivo, como mydevice, haga clic en Comprobar id. para comprobar que el nombre todavía no está en uso y, luego, haga clic en Crear para aprovisionar el dispositivo.

    Agregar id. de dispositivo

  4. Anote las credenciales de dispositivo (id. de dispositivo, nombre de host de IoT Hub y clave de dispositivo). La aplicación cliente necesita estos valores para conectarse con la solución de supervisión remota. A continuación, haga clic en Hecho.

    Ver las credenciales del dispositivo

  5. Seleccione el dispositivo en la lista de dispositivos del panel de la solución. Luego, en el panel Detalles del dispositivo, haga clic en Habilitar dispositivo. El estado del dispositivo ahora es En ejecución. La solución de supervisión remota ahora puede recibir telemetría desde el dispositivo e invocar métodos en el dispositivo.

Creación de una solución de ejemplo de C en Windows

En los siguientes pasos se explica cómo crear una aplicación cliente que se comunique con la solución preconfigurada de supervisión remota. Esta aplicación se escribe en C y se compila y ejecuta en Windows.

Cree un proyecto inicial en Visual Studio 2015 o Visual Studio 2017 y agregue los paquetes NuGet del cliente de dispositivo de IoT Hub:

  1. En Visual Studio, cree una aplicación de consola de C mediante la plantilla Aplicación de consola Win32 de Visual C++. Asigne al proyecto el nombre RMDevice.

  2. En la página Configuración de la aplicación del Asistente para aplicaciones Win32, asegúrese de que se ha seleccionado Aplicación de consola, y desactive la opción Encabezado precompilado y Habilitar comprobaciones adicionales del ciclo de vida de desarrollo de seguridad (SDL).

  3. En el Explorador de soluciones, elimine los archivos stdafx.h, targetver.h y stdafx.cpp.

  4. En el Explorador de soluciones, cambie el nombre del archivo RMDevice.cpp a RMDevice.c.

  5. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto RMDevice y, después, haga clic en Administrar paquetes NuGet. Haga clic en Examinary busque e instale los siguientes paquetes NuGet:

    • Microsoft.Azure.IoTHub.Serializer
    • Microsoft.Azure.IoTHub.IoTHubClient
    • Microsoft.Azure.IoTHub.MqttTransport
  6. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto RMDevice y, después, haga clic en Propiedades para abrir el cuadro de diálogo Páginas de propiedades del proyecto. Para obtener información, consulte Setting Visual C++ Project Properties (Establecimiento de propiedades de proyectos de Visual C++).

  7. Haga clic en la carpeta Vinculador y, luego, haga clic en la página de propiedades Entrada.

  8. Agregue crypt32.lib a la propiedad Dependencias adicionales. Haga clic en Aceptar y, luego, otra vez en Aceptar para guardar los valores de propiedad de proyecto.

Agregue la biblioteca JSON de Parson al proyecto RMDevice y agregue las instrucciones #include necesarias:

  1. En una carpeta adecuada del equipo, clone el repositorio GitHub de Parson mediante el siguiente comando:

    git clone https://github.com/kgabis/parson.git
    
  2. Copie los archivos parson.h y parson.c de la copia local del repositorio de Parson a su carpeta de proyecto RMDevice.

  3. En Visual Studio, haga clic con el botón derecho en el proyecto RMDevice, haga clic en Agregar y luego haga clic en Elemento existente.

  4. En el diálogo Agregar elemento existente, seleccione los archivos parson.h y parson.c en la carpeta de proyecto RMDevice. A continuación, haga clic en Agregar para agregar estos dos archivos al proyecto.

  5. En Visual Studio, abra el archivo RMDevice.c. Sustituya las instrucciones #include existentes por el siguiente código:

    #include "iothubtransportmqtt.h"
    #include "schemalib.h"
    #include "iothub_client.h"
    #include "serializer_devicetwin.h"
    #include "schemaserializer.h"
    #include "azure_c_shared_utility/threadapi.h"
    #include "azure_c_shared_utility/platform.h"
    #include "parson.h"
    

    Nota

    Ahora puede compilar el proyecto para comprobar que tiene configuradas las dependencias correctas.

Especificación del comportamiento del dispositivo IoT

La biblioteca de cliente del serializador de IoT Hub usa un modelo para especificar el formato de los mensajes que el dispositivo intercambia con IoT Hub.

  1. Agregue las siguientes declaraciones de variable después de las instrucciones #include . Sustituya los valores de marcador de posición [Device Id] y [Device Key] por los valores que anotó para el dispositivo en el panel de la solución de supervisión remota. Use el nombre de host de IoT Hub en el panel de la solución para sustituir [IoTHub Name]. Por ejemplo, si el nombre de host de IoT Hub es contoso.azure-devices.net, sustituya [IoTHub Name] por contoso:

    static const char* deviceId = "[Device Id]";
    static const char* connectionString = "HostName=[IoTHub Name].azure-devices.net;DeviceId=[Device Id];SharedAccessKey=[Device Key]";
    
  2. Agregue el código siguiente para definir el modelo que permite que el dispositivo se comunique con IoT Hub. Este modelo especifica que el dispositivo:

    • Puede enviar datos de temperatura, temperatura externa, humedad y un identificador de dispositivo como telemetría.
    • Puede enviar datos sobre el dispositivo a IoT Hub. El dispositivo envía metadatos básicos en un objeto DeviceInfo en el inicio.
    • Puede enviar propiedades notificadas al dispositivo gemelo en IoT Hub. Estas propiedades notificadas se agrupan en propiedades de configuración, dispositivo y sistema.
    • Puede recibir las propiedades deseadas establecidas en el dispositivo gemelo en IoT Hub y actuar en función de ellas.
    • Puede responder a los métodos directos Reboot y InitiateFirmwareUpdate mediante el portal de la solución. El dispositivo envía información sobre los métodos directos que admite mediante las propiedades notificadas.
    // Define the Model
    BEGIN_NAMESPACE(Contoso);
    
    /* Reported properties */
    DECLARE_STRUCT(SystemProperties,
      ascii_char_ptr, Manufacturer,
      ascii_char_ptr, FirmwareVersion,
      ascii_char_ptr, InstalledRAM,
      ascii_char_ptr, ModelNumber,
      ascii_char_ptr, Platform,
      ascii_char_ptr, Processor,
      ascii_char_ptr, SerialNumber
    );
    
    DECLARE_STRUCT(LocationProperties,
      double, Latitude,
      double, Longitude
    );
    
    DECLARE_STRUCT(ReportedDeviceProperties,
      ascii_char_ptr, DeviceState,
      LocationProperties, Location
    );
    
    DECLARE_MODEL(ConfigProperties,
      WITH_REPORTED_PROPERTY(double, TemperatureMeanValue),
      WITH_REPORTED_PROPERTY(uint8_t, TelemetryInterval)
    );
    
    /* Part of DeviceInfo */
    DECLARE_STRUCT(DeviceProperties,
      ascii_char_ptr, DeviceID,
      _Bool, HubEnabledState
    );
    
    DECLARE_DEVICETWIN_MODEL(Thermostat,
      /* Telemetry (temperature, external temperature and humidity) */
      WITH_DATA(double, Temperature),
      WITH_DATA(double, ExternalTemperature),
      WITH_DATA(double, Humidity),
      WITH_DATA(ascii_char_ptr, DeviceId),
    
      /* DeviceInfo */
      WITH_DATA(ascii_char_ptr, ObjectType),
      WITH_DATA(_Bool, IsSimulatedDevice),
      WITH_DATA(ascii_char_ptr, Version),
      WITH_DATA(DeviceProperties, DeviceProperties),
    
      /* Device twin properties */
      WITH_REPORTED_PROPERTY(ReportedDeviceProperties, Device),
      WITH_REPORTED_PROPERTY(ConfigProperties, Config),
      WITH_REPORTED_PROPERTY(SystemProperties, System),
    
      WITH_DESIRED_PROPERTY(double, TemperatureMeanValue, onDesiredTemperatureMeanValue),
      WITH_DESIRED_PROPERTY(uint8_t, TelemetryInterval, onDesiredTelemetryInterval),
    
      /* Direct methods implemented by the device */
      WITH_METHOD(Reboot),
      WITH_METHOD(InitiateFirmwareUpdate, ascii_char_ptr, FwPackageURI),
    
      /* Register direct methods with solution portal */
      WITH_REPORTED_PROPERTY(ascii_char_ptr_no_quotes, SupportedMethods)
    );
    
    END_NAMESPACE(Contoso);
    

Implementación del comportamiento del dispositivo

Ahora, agregue código que implemente el comportamiento definido en el modelo.

  1. Agregue las siguientes funciones que controlan las propiedades deseadas establecidas en el panel de la solución. Estas propiedades deseadas se definen en el modelo:

    void onDesiredTemperatureMeanValue(void* argument)
    {
      /* By convention 'argument' is of the type of the MODEL */
      Thermostat* thermostat = argument;
      printf("Received a new desired_TemperatureMeanValue = %f\r\n", thermostat->TemperatureMeanValue);
    
    }
    
    void onDesiredTelemetryInterval(void* argument)
    {
      /* By convention 'argument' is of the type of the MODEL */
      Thermostat* thermostat = argument;
      printf("Received a new desired_TelemetryInterval = %d\r\n", thermostat->TelemetryInterval);
    }
    
  2. Agregue las funciones siguientes que controlan los métodos directos que se invocan a través de IoT Hub. Estos métodos directos se definen en el modelo:

    /* Handlers for direct methods */
    METHODRETURN_HANDLE Reboot(Thermostat* thermostat)
    {
      (void)(thermostat);
    
      METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Rebooting\"");
      printf("Received reboot request\r\n");
      return result;
    }
    
    METHODRETURN_HANDLE InitiateFirmwareUpdate(Thermostat* thermostat, ascii_char_ptr FwPackageURI)
    {
      (void)(thermostat);
    
      METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Initiating Firmware Update\"");
      printf("Recieved firmware update request. Use package at: %s\r\n", FwPackageURI);
      return result;
    }
    
  3. Agregue la función siguiente que envía un mensaje a la solución preconfigurada:

    /* Send data to IoT Hub */
    static void sendMessage(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size)
    {
      IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size);
      if (messageHandle == NULL)
      {
        printf("unable to create a new IoTHubMessage\r\n");
      }
      else
      {
        if (IoTHubClient_SendEventAsync(iotHubClientHandle, messageHandle, NULL, NULL) != IOTHUB_CLIENT_OK)
        {
          printf("failed to hand over the message to IoTHubClient");
        }
        else
        {
          printf("IoTHubClient accepted the message for delivery\r\n");
        }
    
        IoTHubMessage_Destroy(messageHandle);
      }
      free((void*)buffer);
    }
    
  4. Agregue el siguiente controlador de devolución de llamadas que se ejecuta cuando el dispositivo ha enviado nuevos valores de propiedades notificadas a la solución preconfigurada:

    /* Callback after sending reported properties */
    void deviceTwinCallback(int status_code, void* userContextCallback)
    {
      (void)(userContextCallback);
      printf("IoTHub: reported properties delivered with status_code = %u\n", status_code);
    }
    
  5. Agregue la función siguiente para conectar el dispositivo a la solución preconfiguradas en la nube e intercambiar datos. Esta función lleva a cabo los pasos siguientes:

    • Inicializa la plataforma.
    • Registra el espacio de nombres Contoso con la biblioteca de serialización.
    • Inicializa el cliente con la cadena de conexión del dispositivo.
    • Crea una instancia del modelo del termostato.
    • Crea y envía valores de propiedades notificadas.
    • Envía un objeto DeviceInfo.
    • Crea un bucle para enviar telemetría a cada segundo.
    • Desinicializa todos los recursos.
    void remote_monitoring_run(void)
    {
      if (platform_init() != 0)
      {
        printf("Failed to initialize the platform.\n");
      }
      else
      {
        if (SERIALIZER_REGISTER_NAMESPACE(Contoso) == NULL)
        {
          printf("Unable to SERIALIZER_REGISTER_NAMESPACE\n");
        }
        else
        {
          IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, MQTT_Protocol);
          if (iotHubClientHandle == NULL)
          {
            printf("Failure in IoTHubClient_CreateFromConnectionString\n");
          }
          else
          {
    #ifdef MBED_BUILD_TIMESTAMP
            // For mbed add the certificate information
            if (IoTHubClient_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)
            {
                printf("Failed to set option \"TrustedCerts\"\n");
            }
    #endif // MBED_BUILD_TIMESTAMP
            Thermostat* thermostat = IoTHubDeviceTwin_CreateThermostat(iotHubClientHandle);
            if (thermostat == NULL)
            {
              printf("Failure in IoTHubDeviceTwin_CreateThermostat\n");
            }
            else
            {
              /* Set values for reported properties */
              thermostat->Config.TemperatureMeanValue = 55.5;
              thermostat->Config.TelemetryInterval = 3;
              thermostat->Device.DeviceState = "normal";
              thermostat->Device.Location.Latitude = 47.642877;
              thermostat->Device.Location.Longitude = -122.125497;
              thermostat->System.Manufacturer = "Contoso Inc.";
              thermostat->System.FirmwareVersion = "2.22";
              thermostat->System.InstalledRAM = "8 MB";
              thermostat->System.ModelNumber = "DB-14";
              thermostat->System.Platform = "Plat 9.75";
              thermostat->System.Processor = "i3-7";
              thermostat->System.SerialNumber = "SER21";
              /* Specify the signatures of the supported direct methods */
              thermostat->SupportedMethods = "{\"Reboot\": \"Reboot the device\", \"InitiateFirmwareUpdate--FwPackageURI-string\": \"Updates device Firmware. Use parameter FwPackageURI to specify the URI of the firmware file\"}";
    
              /* Send reported properties to IoT Hub */
              if (IoTHubDeviceTwin_SendReportedStateThermostat(thermostat, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
              {
                printf("Failed sending serialized reported state\n");
              }
              else
              {
                printf("Send DeviceInfo object to IoT Hub at startup\n");
    
                thermostat->ObjectType = "DeviceInfo";
                thermostat->IsSimulatedDevice = 0;
                thermostat->Version = "1.0";
                thermostat->DeviceProperties.HubEnabledState = 1;
                thermostat->DeviceProperties.DeviceID = (char*)deviceId;
    
                unsigned char* buffer;
                size_t bufferSize;
    
                if (SERIALIZE(&buffer, &bufferSize, thermostat->ObjectType, thermostat->Version, thermostat->IsSimulatedDevice, thermostat->DeviceProperties) != CODEFIRST_OK)
                {
                  (void)printf("Failed serializing DeviceInfo\n");
                }
                else
                {
                  sendMessage(iotHubClientHandle, buffer, bufferSize);
                }
    
                /* Send telemetry */
                thermostat->Temperature = 50;
                thermostat->ExternalTemperature = 55;
                thermostat->Humidity = 50;
                thermostat->DeviceId = (char*)deviceId;
    
                while (1)
                {
                  unsigned char*buffer;
                  size_t bufferSize;
    
                  (void)printf("Sending sensor value Temperature = %f, Humidity = %f\n", thermostat->Temperature, thermostat->Humidity);
    
                  if (SERIALIZE(&buffer, &bufferSize, thermostat->DeviceId, thermostat->Temperature, thermostat->Humidity, thermostat->ExternalTemperature) != CODEFIRST_OK)
                  {
                    (void)printf("Failed sending sensor value\r\n");
                  }
                  else
                  {
                    sendMessage(iotHubClientHandle, buffer, bufferSize);
                  }
    
                  ThreadAPI_Sleep(1000);
                }
    
                IoTHubDeviceTwin_DestroyThermostat(thermostat);
              }
            }
            IoTHubClient_Destroy(iotHubClientHandle);
          }
          serializer_deinit();
        }
      }
      platform_deinit();
    }
    

    Como referencia, este es un mensaje de telemetría de ejemplo enviado a la solución preconfigurado:

    {"DeviceId":"mydevice01", "Temperature":50, "Humidity":50, "ExternalTemperature":55}
    

Compilación y ejecución del ejemplo

Agregue código para invocar la función remote_monitoring_run y, a continuación, compile y ejecute la aplicación de dispositivo.

  1. Sustituya la función main por el siguiente código para invocar la función remote_monitoring_run:

    int main()
    {
      remote_monitoring_run();
      return 0;
    }
    
  2. Haga clic en Compilar y, después, en Compilar solución para compilar la aplicación del dispositivo.

  3. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto RMDevice, haga clic en Depurar y, después, en Iniciar nueva instancia para ejecutar el ejemplo. En la consola se muestran mensajes a medida que la aplicación envía telemetría de ejemplo a la solución preconfigurada, recibe valores de propiedad deseados establecidos en el panel de la solución y responde a los métodos invocados desde el panel de la solución.

Ver la telemetría de dispositivo en el panel

El panel de la solución de supervisión remota permite ver la telemetría que los dispositivos envían a IoT Hub.

  1. En el explorador, vuelva al panel de la solución de supervisión remota, haga clic en Dispositivos en el panel izquierdo para navegar hasta la lista de dispositivos.

  2. En la lista de dispositivos, debería ver que el estado del dispositivo es En ejecución. Si no es así, haga clic en Habilitar dispositivo en el panel Detalles del dispositivo.

    Ver el estado del dispositivo

  3. Haga clic en el Panel para volver a él, seleccione el dispositivo en la lista desplegable Device to View (Dispositivo para ver) para ver su telemetría. La telemetría de la aplicación de ejemplo es 50 unidades para la temperatura interior, 55 unidades para la temperatura exterior y 50 unidades para la humedad.

    Visualización de datos de telemetría de dispositivo

Invocar un método en el dispositivo

El panel de la solución de supervisión remota le permite invocar métodos en los dispositivos a través de IoT Hub. Por ejemplo, en la solución de supervisión remota, puede invocar un método para simular que se reinicia un dispositivo.

  1. En el panel de la solución de supervisión remota, haga clic en Devices (Dispositivos) en el panel izquierdo para navegar a la lista de dispositivos.

  2. Haga clic en Device ID (Id. de dispositivo) en el dispositivo de la lista de dispositivos.

  3. En el panel Detalles del dispositivo, haga clic en Métodos.

    Métodos de dispositivo

  4. En el menú desplegable Método, seleccione InitiateFirmwareUpdate y, luego, en FWPACKAGEURI, escriba una dirección URL ficticia. Haga clic en Invocar método para llamar al método en el dispositivo.

    Invocar un método de dispositivo

  5. Puede ver un mensaje en la consola donde se ejecuta el código del dispositivo cuando el dispositivo controla el método. Los resultados del método se agregan al historial en el portal de la solución:

    Ver el historial de métodos

Pasos siguientes

En el artículo Personalización de soluciones preconfiguradas se describen algunas formas de ampliar este ejemplo. Entre las posibles extensiones se incluyen el uso de sensores reales y la implementación de comandos adicionales.

Puede obtener más información sobre los permisos en el sitio azureiotsuite.com.