Tutorial: Configuración de dispositivos desde un servicio back-end

Como parte del ciclo de vida del dispositivo, es posible que tenga que configurar los dispositivos IoT desde el servicio back-end. Cuando envíe una configuración deseada a los dispositivos, también recibirá actualizaciones de estado y cumplimiento de esos mismos dispositivos. Por ejemplo, puede establecer un intervalo de temperatura operativa que desee como objetivo para un dispositivo, o puede recopilar información de versión de firmware de los dispositivos.

Para sincronizar la información de estado entre un dispositivo y un centro de IoT, use dispositivos gemelos. Un dispositivo gemelo es un documento JSON, asociado con un dispositivo específico y almacenado por IoT Hub en la nube en donde puede consultarlo. Un dispositivo gemelo contiene propiedades deseadas, propiedades notificadas y etiquetas.

  • Una propiedad deseada se establece mediante una aplicación back-end y se lee mediante un dispositivo.
  • Una propiedad notificada la establece un dispositivo y la lee una aplicación back-end.
  • Una etiqueta la establece una aplicación back-end y nunca se envía a un dispositivo. Las etiquetas se usan para organizar los dispositivos.

Este tutorial muestra cómo utilizar propiedades deseadas y notificadas para sincronizar la información de estado.

Diagram of device twins on the device and in the cloud.

En este tutorial se realizan las siguientes tareas:

  • Creación de un centro de IoT e incorporación de un dispositivo de prueba en el registro de identidades.
  • Uso de propiedades deseadas para enviar la información de estado para el dispositivo simulado.
  • Uso de propiedades notificadas para recibir la información de estado del dispositivo simulado.

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Requisitos previos

  • En este tutorial se usa la CLI de Azure para crear recursos en la nube. Si ya tiene un centro de IoT con un dispositivo registrado en él, puede omitir esos pasos. Hay dos maneras de ejecutar comandos de la CLI:

  • Las dos aplicaciones de ejemplo que se ejecutan en este tutorial se escriben con Node.js. Necesitará Node.js v10.x.x o una versión posterior en la máquina de desarrollo.

    • Puede descargar Node.js para varias plataformas desde nodejs.org.

    • Puede verificar la versión actual de Node.js en el equipo de desarrollo con el comando siguiente:

      node --version
      
  • Clone o descargue el proyecto de Node.js de ejemplo de Ejemplos de Azure IoT para Node.js.

  • Asegúrese de que está abierto el puerto 8883 del firewall. En el dispositivos de ejemplo de este tutorial se usa el protocolo MQTT, que se comunica mediante el puerto 8883. Este puerto puede estar bloqueado en algunos entornos de red corporativos y educativos. Para más información y para saber cómo solucionar este problema, consulte el artículo sobre la conexión a IoT Hub (MQTT).

Configuración de los recursos de Azure

Para completar este tutorial, su suscripción de Azure tiene que contener un centro de IoT con un dispositivo agregado en el registro de identidad del dispositivo. La entrada en el registro de identidad del dispositivo permite que el dispositivo simulado que se ejecuta en este tutorial se conecte con el centro.

Si aún no tiene un centro de IoT configurado en su suscripción, puede configurar uno con el siguiente script de la CLI. Este script usa el nombre tutorial-iot-hub con un número aleatorio anexado para el nombre del centro de IoT. Puede reemplazar este nombre por su propio nombre único global al ejecutarlo. El script crea el grupo de recursos y el centro en la región Centro de EE. UU. , que usted puede cambiar a otra región más cercana. El script recupera la cadena de conexión de servicio del centro de IoT, que se utiliza en el ejemplo de back-end para conectar con el centro de IoT:

let "randomIdentifier=$RANDOM*$RANDOM"  
hubname="tutorial-iot-hub-$randomIdentifier"
location=centralus

# Install the IoT extension if it's not already installed:
az extension add --name azure-iot

# Create a resource group:
az group create --name tutorial-iot-hub-rg --location $location

# Create your free-tier IoT hub. You can only have one free IoT hub per subscription.
# Change the sku to S1 to create a standard-tier hub if necessary.
az iot hub create --name $hubname --location $location --resource-group tutorial-iot-hub-rg --partition-count 2 --sku F1

# Make a note of the service connection string, you need it later:
az iot hub connection-string show --hub-name $hubname --policy-name service -o table

Este tutorial usa un dispositivo simulado llamado MyTwinDevice. El siguiente script agrega este dispositivo al registro de identidades y recupera su cadena de conexión:

# Create the device in the identity registry:
az iot hub device-identity create --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg

# Retrieve the device connection string, you need this later:
az iot hub device-identity connection-string show --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg -o table

Envío de información de estado a un dispositivo

Para enviar la información de estado de una aplicación back-end a un dispositivo se usan las propiedades deseadas. En esta sección verá como:

  • Configure un dispositivo para recibir y procesar las propiedades deseadas.
  • Envíe las propiedades deseadas desde una aplicación back-end a un dispositivo.

Propiedades deseadas de ejemplo

Puede estructurar las propiedades deseadas de la forma que más le convenga a la aplicación. Este ejemplo utiliza una propiedad de nivel superior denominada fanOn y agrupa las propiedades restantes en diferentes componentes. El siguiente fragmento JSON muestra la estructura de las propiedades deseadas que utiliza este tutorial. El JSON está en el archivo desired.json.

{
  "fanOn": "true",
  "components": {
    "system": {
      "id": "17",
      "units": "farenheit",
      "firmwareVersion": "9.75"
    },
    "wifi" : { 
      "channel" : "6",
      "ssid": "my_network"
    },
    "climate" : {
      "minTemperature": "68",
      "maxTemperature": "76"
    }
  }
}

Recepción de las propiedades deseadas en una aplicación de dispositivo

Para ver el código de ejemplo del dispositivo simulado que recibe las propiedades deseadas, vaya a la carpeta iot-hub/Tutorials/DeviceTwins del proyecto de Node.js de ejemplo que descargó. Luego abra el archivo SimulatedDevice.js. con un editor de texto.

Las secciones siguientes describen el código que se ejecuta en el dispositivo simulado que responde a los cambios de propiedad deseada enviados desde la aplicación back-end.

Recuperación del objeto de dispositivo gemelo

Cuando registró el dispositivo con el centro de IoT, obtuvo una cadena de conexión de dispositivo como salida. El dispositivo usa una cadena de conexión de dispositivo para autenticarse con su identidad registrada en la nube. El código siguiente se conecta con el centro de IoT mediante una cadena de conexión de dispositivo:

// Get the device connection string from a command line argument
var connectionString = process.argv[2];

El código siguiente obtiene un gemelo desde el objeto de cliente:

// Get the device twin
client.getTwin(function(err, twin) {
  if (err) {
    console.error(chalk.red('Could not get device twin'));
  } else {
    console.log(chalk.green('Device twin created'));

Creación de controladores

Puede crear controladores para las actualizaciones de la propiedad deseada que responden a las actualizaciones a diferentes niveles en la jerarquía JSON. Por ejemplo, este controlador ve todos los cambios de propiedad deseada enviados al dispositivo desde una aplicación back-end. La variable delta contiene las propiedades deseadas enviadas desde el back-end de soluciones:

// Handle all desired property updates
twin.on('properties.desired', function(delta) {
    console.log(chalk.yellow('\nNew desired properties received in patch:'));

El siguiente controlador solo responde a los cambios realizados en la propiedad deseada fanOn:

// Handle changes to the fanOn desired property
twin.on('properties.desired.fanOn', function(fanOn) {
    console.log(chalk.green('\nSetting fan state to ' + fanOn));

    // Update the reported property after processing the desired property
    reportedPropertiesPatch.fanOn = fanOn ? fanOn : '{unknown}';
});

Controladores para varias propiedades

En el fragmento JSON de las propiedades deseadas de ejemplo para este tutorial, el nodo climate de components contiene dos propiedades, minTemperature y maxTemperature.

Un objeto gemelo local de un dispositivo almacena un conjunto completo de propiedades deseadas y notificadas. El delta enviado desde el back-end podría actualizar solo un subconjunto de las propiedades deseadas. En el siguiente fragmento de código, si el dispositivo simulado recibe una actualización de una sola de las propiedades minTemperature y maxTemperature, usa el valor en el gemelo local para el otro valor para poder configurar el dispositivo:

// Handle desired properties updates to the climate component
twin.on('properties.desired.components.climate', function(delta) {
    if (delta.minTemperature || delta.maxTemperature) {
      console.log(chalk.green('\nUpdating desired tempertures in climate component:'));
      console.log('Configuring minimum temperature: ' + twin.properties.desired.components.climate.minTemperature);
      console.log('Configuring maximum temperture: ' + twin.properties.desired.components.climate.maxTemperature);

      // Update the reported properties and send them to the hub
      reportedPropertiesPatch.minTemperature = twin.properties.desired.components.climate.minTemperature;
      reportedPropertiesPatch.maxTemperature = twin.properties.desired.components.climate.maxTemperature;
      sendReportedProperties();
    }
});

Tratamiento de las operaciones de inserción, actualización y eliminación

Las propiedades deseadas enviadas desde el back-end no indican qué operación se realiza en una propiedad deseada específica. El código tiene que deducir la operación desde el conjunto actual de propiedades deseadas almacenadas localmente y los cambios que se enviaron desde el centro.

El fragmento de código siguiente muestra cómo el dispositivo simulado controla las operaciones de inserción, actualización y eliminación en la lista de componentes en las propiedades deseadas. Puede ver cómo usar valores null para indicar que un componente debe eliminarse:

// Keep track of all the components the device knows about
var componentList = {};

// Use this componentList list and compare it to the delta to infer
// if anything was added, deleted, or updated.
twin.on('properties.desired.components', function(delta) {
  if (delta === null) {
    componentList = {};
  }
  else {
    Object.keys(delta).forEach(function(key) {

      if (delta[key] === null && componentList[key]) {
        // The delta contains a null value, and the
        // device has a record of this component.
        // Must be a delete operation.
        console.log(chalk.green('\nDeleting component ' + key));
        delete componentList[key];

      } else if (delta[key]) {
        if (componentList[key]) {
          // The delta contains a component, and the
          // device has a record of it.
          // Must be an update operation.
          console.log(chalk.green('\nUpdating component ' + key + ':'));
          console.log(JSON.stringify(delta[key]));
          // Store the complete object instead of just the delta
          componentList[key] = twin.properties.desired.components[key];

        } else {
          // The delta contains a component, and the
          // device has no record of it.
          // Must be an add operation.
          console.log(chalk.green('\nAdding component ' + key + ':'));
          console.log(JSON.stringify(delta[key]));
          // Store the complete object instead of just the delta
          componentList[key] = twin.properties.desired.components[key];
        }
      }
    });
  }
});

Envío de propiedades deseadas desde una aplicación back-end

Ha visto cómo un dispositivo implementa controladores para recibir actualizaciones de propiedad deseada. En esta sección se muestra cómo enviar los cambios de propiedad deseada a un dispositivo desde una aplicación back-end.

Para ver el código de ejemplo del dispositivo simulado que recibe las propiedades deseadas, vaya a la carpeta iot-hub/Tutorials/DeviceTwins del proyecto de Node.js de ejemplo que descargó. Luego abra el archivo ServiceClient.js. con un editor de texto.

El fragmento de código siguiente muestra cómo conectar con el registro de identidad del dispositivo y acceder al gemelo para un dispositivo específico:

// Create a device identity registry object
var registry = Registry.fromConnectionString(connectionString);

// Get the device twin and send desired property update patches at intervals.
// Print the reported properties after some of the desired property updates.
registry.getTwin(deviceId, async (err, twin) => {
  if (err) {
    console.error(err.message);
  } else {
    console.log('Got device twin');

El siguiente fragmento muestra otras revisiones de propiedad deseada que la aplicación back-end envía al dispositivo:

// Turn the fan on
var twinPatchFanOn = {
  properties: {
    desired: {
      patchId: "Switch fan on",
      fanOn: "false",
    }
  }
};

// Set the maximum temperature for the climate component
var twinPatchSetMaxTemperature = {
  properties: {
    desired: {
      patchId: "Set maximum temperature",
      components: {
        climate: {
          maxTemperature: "92"
        }
      }
    }
  }
};

// Add a new component
var twinPatchAddWifiComponent = {
  properties: {
    desired: {
      patchId: "Add WiFi component",
      components: {
        wifi: { 
          channel: "6",
          ssid: "my_network"
        }
      }
    }
  }
};

// Update the WiFi component
var twinPatchUpdateWifiComponent = {
  properties: {
    desired: {
      patchId: "Update WiFi component",
      components: {
        wifi: { 
          channel: "13",
          ssid: "my_other_network"
        }
      }
    }
  }
};

// Delete the WiFi component
var twinPatchDeleteWifiComponent = {
  properties: {
    desired: {
      patchId: "Delete WiFi component",
      components: {
        wifi: null
      }
    }
  }
};

El siguiente fragmento muestra cómo la aplicación back-end envía una actualización de propiedad deseada a un dispositivo:

// Send a desired property update patch
async function sendDesiredProperties(twin, patch) {
  twin.update(patch, (err, twin) => {
    if (err) {
      console.error(err.message);
    } else {
      console.log(chalk.green(`\nSent ${twin.properties.desired.patchId} patch:`));
      console.log(JSON.stringify(patch, null, 2));
    }
  });
}

Recepción de información de estado de un dispositivo

La aplicación back-end recibe información de estado de un dispositivo como propiedades notificadas. Un dispositivo establece las propiedades notificadas y las envía al centro. Una aplicación back-end puede leer los valores actuales de las propiedades notificadas desde el dispositivo gemelo almacenado en el centro.

Envío de propiedades notificadas desde un dispositivo

Puede enviar actualizaciones a valores de propiedad notificada tales como una revisión. El siguiente fragmento muestra una plantilla para la revisión que envía el dispositivo simulado. El dispositivo simulado actualiza los campos de la revisión antes de enviarla al centro:

// Create a patch to send to the hub
var reportedPropertiesPatch = {
  firmwareVersion:'1.2.1',
  lastPatchReceivedId: '',
  fanOn:'',
  minTemperature:'',
  maxTemperature:''
};

El dispositivo simulado utiliza la siguiente función para enviar la revisión que contiene las propiedades notificadas al centro:

// Send the reported properties patch to the hub
function sendReportedProperties() {
  twin.properties.reported.update(reportedPropertiesPatch, function(err) {
    if (err) throw err;
    console.log(chalk.blue('\nTwin state reported'));
    console.log(JSON.stringify(reportedPropertiesPatch, null, 2));
  });
}

Procesamiento de propiedades notificadas

Una aplicación back-end tiene acceso a los valores de propiedad notificada actuales para un dispositivo a través del dispositivo gemelo. El siguiente fragmento muestra cómo la aplicación back-end lee los valores de propiedad notificada para el dispositivo simulado:

// Display the reported properties from the device
function printReportedProperties(twin) {
  console.log("Last received patch: " + twin.properties.reported.lastPatchReceivedId);
  console.log("Firmware version: " + twin.properties.reported.firmwareVersion);
  console.log("Fan status: " + twin.properties.reported.fanOn);
  console.log("Min temperature set: " + twin.properties.reported.minTemperature);
  console.log("Max temperature set: " + twin.properties.reported.maxTemperature);
}

Ejecución de las aplicaciones

En esta sección, se ejecutan las dos aplicaciones de ejemplo para observar como una aplicación back-end envía las actualizaciones de propiedad deseada a una aplicación de dispositivo simulado.

Para ejecutar el dispositivo simulado y las aplicaciones back-end, necesita las cadenas de conexión de dispositivo y servicio. Cuando creó los recursos al principio de este tutorial, tomó nota de las cadenas de conexión.

Para ejecutar la aplicación de dispositivo simulado, abra una ventana del shell o del símbolo del sistema y vaya a la carpeta iot-hub/Tutorials/DeviceTwins en el proyecto Node.js que descargó. Luego, ejecute los siguientes comandos:

npm install
node SimulatedDevice.js "{your device connection string}"

Para ejecutar la aplicación back-end, abra otra ventana del shell o del símbolo del sistema. A continuación, vaya a la carpeta iot-hub/Tutorials/DeviceTwins del proyecto de Node.js que ha descargado. Luego, ejecute los siguientes comandos:

npm install
node ServiceClient.js "{your service connection string}"

Observación de las actualizaciones de propiedades deseadas

La siguiente captura de pantalla muestra el resultado de la aplicación del dispositivo simulado y resalta cómo trata una actualización de la propiedad deseada maxTemperature. Puede ver cómo se ejecutan el controlador de nivel superior y los controladores del componente climate:

Screenshot that shows how both the top-level handler and the climate component handlers run.

La siguiente captura de pantalla muestra el resultado de la aplicación back-end y resalta cómo envía una actualización a la propiedad deseada maxTemperature:

Screenshot that shows the output from the back-end application and highlights how it sends an update.

Observación de las actualizaciones de propiedades notificadas

La siguientes captura de pantalla muestra el resultado de la aplicación de dispositivo simulado y resalta cómo envía una actualización de la propiedad notificada al centro:

Screenshot that shows the simulated device updating its twin state.

La siguiente captura de pantalla muestra la salida de la aplicación back-end y resalta cómo recibe y procesa una actualización de la propiedad notificada desde un dispositivo:

Screenshot that shows the back-end application receiving the device reported properties.

Limpieza de recursos

Si tiene pensado completar el siguiente tutorial, deje el centro de IoT y el grupo de recursos para volver a usarlos más tarde.

Si ya no los va a necesitar más, elimínelos en el portal. Para ello, seleccione el grupo de recursos tutorial-iot-hub-rg que contiene el centro de IoT y seleccione Eliminar.

Como alternativa, use la CLI:

# Delete your resource group and its contents
az group delete --name tutorial-iot-hub-rg

Pasos siguientes

En este tutorial, ha aprendido a sincronizar la información de estado entre los dispositivos y el centro de IoT. Avance hasta el siguiente tutorial para aprender a usar dispositivos gemelos a fin de implementar un proceso de actualización del dispositivo.