Esercitazione: Configurare i dispositivi da un servizio back-end

Nell'ambito del ciclo di vita del dispositivo, potrebbe essere necessario configurare i dispositivi IoT dal servizio back-end. Quando si invia una configurazione desiderata ai dispositivi, si vogliono ricevere anche gli aggiornamenti di stato e conformità da tali dispositivi. Ad esempio, è possibile impostare un intervallo di temperatura operativa di destinazione per un dispositivo o raccogliere informazioni sulla versione del firmware dai dispositivi.

Per sincronizzare le informazioni sullo stato tra un dispositivo e un hub IoT, si usano dispositivi gemelli. Un dispositivo gemello è un documento JSON, associato con un dispositivo specifico e archiviato dall'hub IoT nel cloud, in cui è possibile eseguire query. Un dispositivo gemello contiene proprietà desiderate, proprietà segnalate e tag.

  • Una proprietà desiderata viene impostata da un'applicazione back-end e letta da un dispositivo.
  • Una proprietà segnalata viene impostata da un dispositivo e letta da un'applicazione back-end.
  • Un tag viene impostato da un'applicazione back-end e non viene mai inviato a un dispositivo. I tag vengono usati per organizzare i dispositivi.

Questa esercitazione illustra come usare le proprietà desiderate e segnalate per sincronizzare le informazioni sullo stato.

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

In questa esercitazione si eseguono le seguenti attività:

  • Creare un hub IoT e aggiungere un dispositivo di test nel registro delle identità.
  • Usare le proprietà desiderate per inviare informazioni sullo stato al dispositivo simulato.
  • Usare le proprietà segnalate per ricevere informazioni sullo stato dal dispositivo simulato.

Se non si ha una sottoscrizione di Azure, creare un account gratuito prima di iniziare.

Prerequisiti

  • Questa esercitazione usa l'interfaccia della riga di comando di Azure per creare risorse cloud. Se è già disponibile un hub IoT con un dispositivo registrato, è possibile ignorare questi passaggi. Esistono due modi per eseguire i comandi dell'interfaccia della riga di comando:

  • Le due applicazioni di esempio eseguite in questa esercitazione vengono scritte usando Node.js. È necessario disporre di Node.js v10.x.x o versioni successive nel computer di sviluppo.

    • È possibile scaricare Node.js per più piattaforme da nodejs.org.

    • È possibile verificare la versione corrente di Node.js installata nel computer di sviluppo tramite il comando seguente:

      node --version
      
  • Clonare o scaricare il progetto di esempio Node.js dagli esempi di Azure IoT per Node.js.

  • Assicurarsi che la porta 8883 sia aperta nel firewall. L'esempio di dispositivo di questa esercitazione usa il protocollo MQTT, che comunica tramite la porta 8883. Questa porta potrebbe essere bloccata in alcuni ambienti di rete aziendali e didattici. Per altre informazioni e soluzioni alternative per questo problema, vedere Connettersi all'hub IoT (MQTT).

Configurare le risorse di Azure

Per completare questa esercitazione, la sottoscrizione di Azure deve contenere un hub IoT con un dispositivo aggiunto al registro delle identità del dispositivo. La voce del registro delle identità del dispositivo consente al dispositivo simulato in esecuzione in questa esercitazione di connettersi all'hub.

Se non è già presente un hub IoT nella sottoscrizione, è possibile configurarne uno con lo script dell'interfaccia della riga di comando seguente. Questo script usa il nome tutorial-iot-hub con un numero casuale aggiunto per il nome dell'hub IoT. È possibile sostituire questo nome con il proprio nome univoco globale quando viene eseguito. Lo script crea il gruppo di risorse e l'hub nell'area Stati Uniti centrali, che è possibile sostituire con un'area più vicina all'utente. Lo script recupera la stringa di connessione del servizio hub IoT, utilizzabile nell'esempio di back-end per la connessione all'hub 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

Questa esercitazione usa un dispositivo simulato chiamato MyTwinDevice. Lo script seguente aggiunge questo dispositivo nel registro delle identità e ne recupera la stringa di connessione:

# 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

Inviare informazioni sullo stato a un dispositivo

Le proprietà desiderate vengono usate per inviare informazioni sullo stato da un'applicazione back-end a un dispositivo. In questa sezione verrà illustrato come:

  • Configurare un dispositivo per ricevere ed elaborare le proprietà desiderate.
  • Inviare le proprietà desiderate da un'applicazione back-end a un dispositivo.

Proprietà desiderate di esempio

È possibile strutturare le proprietà desiderate in modo utile all'applicazione. Questo esempio usa una proprietà di livello superiore chiamata fanOn e raggruppa le proprietà restanti in componenti distinti. Il frammento JSON seguente illustra la struttura delle proprietà desiderate usate in questa esercitazione. Il codice JSON si trova nel file desired.json.

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

Ricevere le proprietà desiderate in un'applicazione del dispositivo

Per visualizzare il codice di esempio del dispositivo simulato che riceve le proprietà desiderate, passare alla cartella iot-hub/Tutorials/DeviceTwins nel progetto di esempio di Node.js scaricato. Aprire quindi il file SimulatedDevice.js in un editor di testo.

Le sezioni seguenti descrivono il codice eseguito nel dispositivo simulato che risponde alle modifiche delle proprietà desiderate inviate dall'applicazione back-end.

Recuperare l'oggetto dispositivo gemello

Quando il dispositivo è stato registrato con l'hub IoT, si riceve un dispositivo stringa di connessione come output. Un dispositivo stringa di connessione viene usato dal dispositivo per l'autenticazione con la relativa identità registrata nel cloud. Il codice seguente si connette all'hub IoT tramite una stringa di connessione del dispositivo:

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

Il codice seguente ottiene un dispositivo gemello dall'oggetto client:

// 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'));

Creare gestori

È possibile creare gestori per gli aggiornamenti delle proprietà desiderate che rispondono agli aggiornamenti a livelli diversi della gerarchia JSON. Ad esempio, questo gestore rileva tutte le modifiche delle proprietà desiderate inviate al dispositivo da un'applicazione back-end. La variabile delta contiene le proprietà desiderate inviate dal back-end della soluzione:

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

Il gestore seguente reagisce solo alle modifiche apportate alla proprietà desiderata 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}';
});

Gestori per più proprietà

Nel codice JSON delle proprietà desiderate di esempio per questa esercitazione, il nodo clima nei componenti contiene due proprietà, minTemperature e maxTemperature.

Un oggetto twin locale del dispositivo archivia un set completo di proprietà desiderate e segnalate. Il delta inviato dal back-end potrebbe aggiornare solo un subset di proprietà desiderate. Nel frammento di codice seguente, se il dispositivo simulato riceve un aggiornamento per un solo valore tra minTemperature e maxTemperature, viene usato il valore nel dispositivo gemello locale per l'altro valore per configurare il 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();
    }
});

Gestire le operazioni di inserimento, aggiornamento ed eliminazione

Le proprietà desiderate inviate dal back-end non indicano quale operazione viene eseguita su una determinata proprietà desiderata. Il codice deve essere in grado di dedurre l'operazione dal set corrente di proprietà desiderate archiviate in locale e dalle modifiche inviate dall'hub.

Il frammento di codice seguente mostra in che modo il dispositivo simulato gestisce le operazioni di inserimento, aggiornamento ed eliminazione nell'elenco dei componenti nelle proprietà desiderate. È possibile vedere come usare i valori null per indicare che un componente deve essere eliminato:

// 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];
        }
      }
    });
  }
});

Inviare le proprietà desiderate da un'applicazione back-end

Si è visto come un dispositivo implementa i gestori per ricevere gli aggiornamenti delle proprietà desiderate. Questa sezione illustra come inviare modifiche delle proprietà desiderate a un dispositivo da un'applicazione back-end.

Per visualizzare il codice di esempio del dispositivo simulato che riceve le proprietà desiderate, passare alla cartella iot-hub/Tutorials/DeviceTwins nel progetto di esempio di Node.js scaricato. Aprire quindi il file ServiceClient.js in un editor di testo.

Il frammento di codice seguente mostra come connettersi al registro delle identità del dispositivo e accedere al dispositivo gemello per un dispositivo specifico:

// 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');

Il frammento di codice seguente mostra diverse patch delle proprietà desiderate che l'applicazione back-end invia 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
      }
    }
  }
};

Il frammento di codice seguente illustra in che modo l'applicazione back-end invia un aggiornamento delle proprietà desiderate 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));
    }
  });
}

Ricevere informazioni sullo stato da un dispositivo

L'applicazione back-end riceve le informazioni sullo stato da un dispositivo come proprietà segnalate. Un dispositivo imposta le proprietà segnalate e le invia all'hub. Un'applicazione back-end è in grado di leggere i valori correnti delle proprietà segnalate da un dispositivo gemello archiviato nell'hub.

Inviare le proprietà segnalate da un dispositivo

È possibile inviare aggiornamenti ai valori delle proprietà segnalate come patch. Il frammento di codice seguente mostra un modello per la patch che il dispositivo simulato invia. Il dispositivo simulato aggiorna i campi nella patch prima di inviarla all'hub:

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

Il dispositivo simulato usa la funzione seguente per inviare la patch contenente le proprietà segnalate all'hub:

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

Elaborare le proprietà segnalate

Un'applicazione back-end accede ai valori correnti delle proprietà segnalate per un dispositivo tramite il dispositivo gemello. Il frammento di codice seguente mostra come l'applicazione back-end legge i valori delle proprietà segnalate per il dispositivo simulato:

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

Eseguire le applicazioni

In questa sezione vengono eseguite le due applicazioni di esempio da osservare come un'applicazione back-end invia gli aggiornamenti delle proprietà desiderati a un'applicazione del dispositivo simulato.

Per eseguire le applicazioni back-end e del dispositivo simulato, sono necessarie le stringhe di connessione del servizio e del dispositivo. Le stringhe di connessione sono state annotate in fase di creazione delle risorse all'inizio di questa esercitazione.

Per eseguire l'applicazione del dispositivo simulato, aprire una shell o una finestra del prompt dei comandi e passare alla cartella iot-hub/Tutorials/DeviceTwins nel progetto di Node.js scaricato. Eseguire quindi i comandi seguenti:

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

Per eseguire l'applicazione back-end, aprire un'altra shell o un'altra finestra del prompt dei comandi. Passare quindi alla cartella iot-hub/Tutorials/DeviceTwins nel progetto di Node.js scaricato. Eseguire quindi i comandi seguenti:

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

Osservare gli aggiornamenti delle proprietà desiderati

La schermata seguente mostra l'output dell'applicazione del dispositivo simulato ed evidenzia come viene gestito l'aggiornamento della proprietà desiderata maxTemperature. È possibile vedere in che modo vengono eseguiti i gestori del componente relativo alle condizioni climatiche e del gestore di livello superiore:

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

La schermata seguente mostra l'output dell'applicazione back-end ed evidenzia come viene inviato l'aggiornamento della proprietà desiderata maxTemperature:

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

Osservare gli aggiornamenti delle proprietà segnalate

La schermata seguente mostra l'output dell'applicazione del dispositivo simulato ed evidenzia come viene inviato all'hub un aggiornamento delle proprietà segnalate:

Screenshot that shows the simulated device updating its twin state.

La schermata seguente mostra l'output dell'applicazione back-end ed evidenzia come viene ricevuto ed elaborato un aggiornamento delle proprietà segnalate da un dispositivo:

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

Pulire le risorse

Se si prevede di completare l'esercitazione successiva, lasciare il gruppo di risorse e l'hub IoT per riutilizzarli in un secondo momento.

Se l'hub IoT non è più necessario, eliminarlo insieme al gruppo di risorse nel portale, A tale scopo, selezionare il gruppo di risorse tutorial-iot-hub-rg che contiene l'hub IoT e selezionare Elimina.

In alternativa, usare l'interfaccia della riga di comando:

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

Passaggi successivi

In questa esercitazione si è appreso come sincronizzare le informazioni sullo stato tra i dispositivi e l'hub IoT. Passare all'esercitazione successiva per informazioni su come usare i dispositivi gemelli per implementare il processo di aggiornamento del dispositivo.