Connecter votre appareil Raspberry Pi à l’accélérateur de solution de supervision à distance (C)

Dans ce tutoriel, vous allez implémenter un appareil Condenseur qui envoie les données de télémétrie ci-après à l’accélérateur de solution Monitoring à distance :

  • Température
  • Pression
  • Humidité

Par souci de simplicité, le code génère des exemples de valeurs de données de télémétrie pour le Condenseur. Vous pouviez étendre l’exemple en connectant des capteurs réels sur votre périphérique et envoyer les données de télémétrie réelles.

Le périphérique en exemple également :

  • Envoie des métadonnées à la solution pour décrire ses fonctionnalités.
  • Répond aux actions déclenchées à partir de la page Périphériques dans la solution.
  • Répond aux modifications de configuration envoyées à partir de la page Périphériques dans la solution.

Pour effectuer ce didacticiel, vous avez besoin d’un compte Azure actif. Si vous ne possédez pas de compte, vous pouvez créer un compte d’évaluation gratuit en quelques minutes. Pour plus d’informations, consultez Essai gratuit Azure.

Avant de commencer

Avant d’écrire du code pour votre appareil, déployez votre accélérateur de solution de supervision à distance, puis ajoutez un nouvel appareil réel à cette solution.

Déployer votre accélérateur de solution de supervision à distance

L’appareil Condenseur que vous créez dans le cadre de ce tutoriel envoie des données à une instance de l’accélérateur de solution Monitoring à distance. Si vous n’avez pas déjà provisionné l’accélérateur de solution de supervision à distance dans votre compte Azure, consultez Déployer l’accélérateur de solution de supervision à distance.

Au terme du processus de déploiement de la solution de supervision à distance, cliquez sur Lancer pour ouvrir le tableau de bord de la solution dans votre navigateur.

Tableau de bord de la solution

Ajouter votre appareil à la solution de supervision à distance

Notes

Si vous avez déjà ajouté un appareil dans votre solution, vous pouvez ignorer cette étape. Toutefois, l’étape suivante requiert la chaîne de connexion de votre appareil. Vous pouvez récupérer la chaîne de connexion d’un appareil à partir du portail Azure ou à l’aide de l’outil d’interface de ligne de commande (CLI) az iot.

Pour qu’un appareil puisse se connecter à l’accélérateur de solution, il doit s’identifier auprès d’IoT Hub à l’aide d’informations d’identification valides. Vous avez la possibilité d’enregistrer la chaîne de connexion de l’appareil qui contient ces informations d’identification lorsque vous ajoutez l’appareil à la solution. La chaîne de connexion de l’appareil sera ajoutée dans votre application cliente dans la suite de ce didacticiel.

Pour ajouter un appareil à votre solution de supervision à distance, procédez comme suit dans la page Explorateur d’appareils de la solution :

  1. Choisissez + Nouvel appareil, puis Réel comme Type d’appareil :

    Ajouter un appareil réel

  2. Entrez Physique-condenseur comme ID de périphérique. Choisissez les options Clé symétrique et Générer automatiquement des clés :

    Choisissez les options de périphérique

  3. Choisissez Appliquer. Notez quelque part les valeurs de l’ID de l’appareil, de la Clé primaire et de la Clé primaire de la chaîne de connexion :

    Récupérer les informations d’identification

Vous venez d’ajouter un appareil réel à l’accélérateur de solution de supervision à distance et de noter sa chaîne de connexion d’appareil. Dans les sections suivantes, vous allez implémenter l’application cliente qui utilise la chaîne de connexion de l’appareil pour se connecter à votre solution.

L’application cliente met en œuvre le modèle de périphérique Condenseur intégré. Un modèle d’appareil d’accélérateur de solution spécifie les éléments ci-après concernant un appareil :

  • Les propriétés que le périphérique signale à la solution. Par exemple, un périphérique Condenseur fournit des informations sur son microprogramme et son emplacement.
  • Les types de données de télémétrie envoyés par le périphérique à la solution. Par exemple, un périphérique Condenseur envoie des valeurs sur la température, l’humidité et la pression.
  • Les méthodes que vous pouvez planifier à partir de la solution pour s’exécuter sur le périphérique. Par exemple, un périphérique Condenseur doit mettre en œuvre des méthodes Redémarrer, FirmwareUpdate, EmergencyValveRelease et IncreasePressure.

Ce tutoriel vous montre comment connecter un appareil réel à l’accélérateur de solution de supervision à distance. Comme avec la plupart des applications embarquées qui s’exécutent sur des appareils limités, le code client pour l’application d’appareil Raspberry Pi est écrit en C. Dans ce didacticiel, vous générez l’application sur un appareil Raspberry Pi exécutant le système d’exploitation Raspbian.

Si vous préférez simuler un appareil, consultez Créer et tester un appareil simulé.

Matériel requis

Un ordinateur de bureau permettant de vous connecter à distance à la ligne de commande sur le Raspberry Pi.

Starter Kit Microsoft IoT pour Raspberry Pi 3 ou composants équivalents. Ce didacticiel utilise les éléments suivants du kit :

  • Raspberry Pi 3
  • Carte MicroSD (avec NOOBS)
  • Un câble mini USB
  • Un câble Ethernet

Logiciels de bureau requis

Vous devez installer le client SSH sur votre ordinateur de bureau afin de pouvoir accéder à distance à la ligne de commande sur le Raspberry Pi.

  • Windows n’inclut pas de client SSH. Nous vous recommandons d’utiliser PuTTY.
  • La plupart des distributions Linux et Mac OS incluent l’utilitaire de ligne de commande SSH. Pour plus d’informations, consultez SSH Using Linux or Mac OS (Utilisation de SSH avec Linux ou Mac OS).

Logiciels Raspberry Pi requis

Cet article suppose que vous avez installé la dernière version du système d’exploitation Raspbian sur votre appareil Raspberry Pi.

Les étapes suivantes vous montrent comment préparer votre appareil Raspberry Pi pour générer une application C qui se connecte à l’accélérateur de solution :

  1. Connectez-vous à votre appareil Raspberry Pi avec ssh. Pour plus d’informations, consultez SSH (Secure Shell) sur le site web de Raspberry Pi.

  2. Pour mettre à jour votre Raspberry Pi, utilisez la commande suivante :

    sudo apt-get update
    
  3. Pour effectuer les étapes décrites dans ce guide pratique, suivez les étapes de Configurer votre environnement de développement Linux pour ajouter les outils et bibliothèques de développement requis pour votre Raspberry Pi.

Afficher le code

L’exemple de code utilisé dans ce guide est disponible dans le référentiel GitHub des kits de développement logiciel (SDK) Azure IoT C.

Télécharger le code source et préparer le projet

Pour préparer le projet, clonez ou téléchargez le référentiel de kits de développement logiciel (SDK) Azure IoT C à partir de GitHub.

L’exemple se trouve dans le dossier samples/solutions/remote_monitoring_client.

Ouvrez le fichier remote_monitoring.c dans le dossier samples/solutions/remote_monitoring_client dans un éditeur de texte.

Vérification du code

Cette section décrit certaines parties essentielles de l’exemple de code et explique comment elles sont liées à l’accélérateur de solution de supervision à distance.

L’extrait de code suivant montre comment les propriétés signalées qui décrivent les fonctionnalités de l’appareil sont définies. Ces propriétés sont les suivantes :

  • Emplacement de l’appareil pour permettre à l’accélérateur de solution d’ajouter l’appareil à la carte.
  • Version actuelle du microprogramme.
  • Liste des méthodes prises en charge par l’appareil.
  • Schéma des messages de télémétrie envoyés par l’appareil.
typedef struct MESSAGESCHEMA_TAG
{
    char* name;
    char* format;
    char* fields;
} MessageSchema;

typedef struct TELEMETRYSCHEMA_TAG
{
    MessageSchema messageSchema;
} TelemetrySchema;

typedef struct TELEMETRYPROPERTIES_TAG
{
    TelemetrySchema temperatureSchema;
    TelemetrySchema humiditySchema;
    TelemetrySchema pressureSchema;
} TelemetryProperties;

typedef struct CHILLER_TAG
{
    // Reported properties
    char* protocol;
    char* supportedMethods;
    char* type;
    char* firmware;
    FIRMWARE_UPDATE_STATUS firmwareUpdateStatus;
    char* location;
    double latitude;
    double longitude;
    TelemetryProperties telemetry;

    // Manage firmware update process
    char* new_firmware_version;
    char* new_firmware_URI;
} Chiller;

L’exemple inclut une fonction serializeToJson qui sérialise cette structure de données à l’aide de la bibliothèque Parson.

L’exemple inclut plusieurs fonctions de rappel qui affichent les informations sur la console lorsque le client interagit avec l’accélérateur de solution :

  • connection_status_callback
  • send_confirm_callback
  • reported_state_callback
  • device_method_callback

L’extrait de code suivant montre la fonction device_method_callback. Cette fonction détermine l’action à entreprendre lorsqu’un appel de méthode est reçu à partir de l’accélérateur de solution. La fonction reçoit une référence à la structure de données Chiller dans le paramètre userContextCallback. La valeur de userContextCallback est définie lorsque la fonction de rappel est configurée dans la fonction main :

static int device_method_callback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* response_size, void* userContextCallback)
{
    Chiller *chiller = (Chiller *)userContextCallback;

    int result;

    (void)printf("Direct method name:    %s\r\n", method_name);

    (void)printf("Direct method payload: %.*s\r\n", (int)size, (const char*)payload);

    if (strcmp("Reboot", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Rebooting\" }")
    }
    else if (strcmp("EmergencyValveRelease", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Releasing emergency valve\" }")
    }
    else if (strcmp("IncreasePressure", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Increasing pressure\" }")
    }
    else if (strcmp("FirmwareUpdate", method_name) == 0)
    {
        if (chiller->firmwareUpdateStatus != IDLE)
        {
            (void)printf("Attempt to invoke firmware update out of order\r\n");
            MESSAGERESPONSE(400, "{ \"Response\": \"Attempting to initiate a firmware update out of order\" }")
        }
        else
        {
            getFirmwareUpdateValues(chiller, payload);

            if (chiller->new_firmware_version != NULL && chiller->new_firmware_URI != NULL)
            {
                // Create a thread for the long-running firmware update process.
                THREAD_HANDLE thread_apply;
                THREADAPI_RESULT t_result = ThreadAPI_Create(&thread_apply, do_firmware_update, chiller);
                if (t_result == THREADAPI_OK)
                {
                    (void)printf("Starting firmware update thread\r\n");
                    MESSAGERESPONSE(201, "{ \"Response\": \"Starting firmware update thread\" }")
                }
                else
                {
                    (void)printf("Failed to start firmware update thread\r\n");
                    MESSAGERESPONSE(500, "{ \"Response\": \"Failed to start firmware update thread\" }")
                }
            }
            else
            {
                (void)printf("Invalid method payload\r\n");
                MESSAGERESPONSE(400, "{ \"Response\": \"Invalid payload\" }")
            }
        }
    }
    else
    {
        // All other entries are ignored.
        (void)printf("Method not recognized\r\n");
        MESSAGERESPONSE(400, "{ \"Response\": \"Method not recognized\" }")
    }

    return result;
}

Lorsque l’accélérateur de solution appelle la méthode de mise à jour du microprogramme, l’exemple désérialise la charge utile JSON et démarre un thread d’arrière-plan pour terminer le processus de mise à jour. L’extrait de code suivant montre do_firmware_update, qui s’exécute sur le thread :

/*
 This is a thread allocated to process a long-running device method call.
 It uses device twin reported properties to communicate status values
 to the Remote Monitoring solution accelerator.
*/
static int do_firmware_update(void *param)
{
    Chiller *chiller = (Chiller *)param;
    printf("Running simulated firmware update: URI: %s, Version: %s\r\n", chiller->new_firmware_URI, chiller->new_firmware_version);

    printf("Simulating download phase...\r\n");
    chiller->firmwareUpdateStatus = DOWNLOADING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    printf("Simulating apply phase...\r\n");
    chiller->firmwareUpdateStatus = APPLYING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    printf("Simulating reboot phase...\r\n");
    chiller->firmwareUpdateStatus = REBOOTING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    size_t size = strlen(chiller->new_firmware_version) + 1;
    (void)memcpy(chiller->firmware, chiller->new_firmware_version, size);

    chiller->firmwareUpdateStatus = IDLE;
    sendChillerReportedProperties(chiller);

    return 0;
}

L’extrait de code suivant montre comment le client envoie un message de télémétrie à l’accélérateur de solution. Les propriétés du message incluent le schéma du message qui aide l’accélérateur de solution à afficher les données de télémétrie sur le tableau de bord :

static void send_message(IOTHUB_DEVICE_CLIENT_HANDLE handle, char* message, char* schema)
{
    IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(message);
    if (message_handle != NULL)
    {
        // Set system properties
        (void)IoTHubMessage_SetMessageId(message_handle, "MSG_ID");
        (void)IoTHubMessage_SetCorrelationId(message_handle, "CORE_ID");
        (void)IoTHubMessage_SetContentTypeSystemProperty(message_handle, "application%2fjson");
        (void)IoTHubMessage_SetContentEncodingSystemProperty(message_handle, "utf-8");

        // Set application properties
        MAP_HANDLE propMap = IoTHubMessage_Properties(message_handle);
        (void)Map_AddOrUpdate(propMap, "$$MessageSchema", schema);
        (void)Map_AddOrUpdate(propMap, "$$ContentType", "JSON");

        time_t now = time(0);
        struct tm* timeinfo;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4996) /* Suppress warning about possible unsafe function in Visual Studio */
#endif
        timeinfo = gmtime(&now);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
        char timebuff[50];
        strftime(timebuff, 50, "%Y-%m-%dT%H:%M:%SZ", timeinfo);
        (void)Map_AddOrUpdate(propMap, "$$CreationTimeUtc", timebuff);

        IoTHubDeviceClient_SendEventAsync(handle, message_handle, send_confirm_callback, NULL);

        IoTHubMessage_Destroy(message_handle);
    }
}

La fonction main dans l’échantillon :

  • Initialise et arrête le sous-système du kit de développement logiciel (SDK).
  • Initialise la structure de données Chiller.
  • Envoie les propriétés signalées à l’accélérateur de solution.
  • Configure la fonction de rappel de méthode de l’appareil.
  • Envoie des valeurs de télémétrie simulées à l’accélérateur de solution.
int main(void)
{
    srand((unsigned int)time(NULL));
    double minTemperature = 50.0;
    double minPressure = 55.0;
    double minHumidity = 30.0;
    double temperature = 0;
    double pressure = 0;
    double humidity = 0;

    (void)printf("This sample simulates a Chiller device connected to the Remote Monitoring solution accelerator\r\n\r\n");

    // Used to initialize sdk subsystem
    (void)IoTHub_Init();

    (void)printf("Creating IoTHub handle\r\n");
    // Create the iothub handle here
    device_handle = IoTHubDeviceClient_CreateFromConnectionString(connectionString, MQTT_Protocol);
    if (device_handle == NULL)
    {
        (void)printf("Failure creating IotHub device. Hint: Check your connection string.\r\n");
    }
    else
    {
        // Setting connection status callback to get indication of connection to iothub
        (void)IoTHubDeviceClient_SetConnectionStatusCallback(device_handle, connection_status_callback, NULL);

        Chiller chiller;
        memset(&chiller, 0, sizeof(Chiller));
        chiller.protocol = "MQTT";
        chiller.supportedMethods = "Reboot,FirmwareUpdate,EmergencyValveRelease,IncreasePressure";
        chiller.type = "Chiller";
        size_t size = strlen(initialFirmwareVersion) + 1;
        chiller.firmware = malloc(size);
        if (chiller.firmware == NULL)
        {
            (void)printf("Chiller Firmware failed to allocate memory.\r\n");
        }
        else
        {
            memcpy(chiller.firmware, initialFirmwareVersion, size);
            chiller.firmwareUpdateStatus = IDLE;
            chiller.location = "Building 44";
            chiller.latitude = 47.638928;
            chiller.longitude = -122.13476;
            chiller.telemetry.temperatureSchema.messageSchema.name = "chiller-temperature;v1";
            chiller.telemetry.temperatureSchema.messageSchema.format = "JSON";
            chiller.telemetry.temperatureSchema.messageSchema.fields = "{\"temperature\":\"Double\",\"temperature_unit\":\"Text\"}";
            chiller.telemetry.humiditySchema.messageSchema.name = "chiller-humidity;v1";
            chiller.telemetry.humiditySchema.messageSchema.format = "JSON";
            chiller.telemetry.humiditySchema.messageSchema.fields = "{\"humidity\":\"Double\",\"humidity_unit\":\"Text\"}";
            chiller.telemetry.pressureSchema.messageSchema.name = "chiller-pressure;v1";
            chiller.telemetry.pressureSchema.messageSchema.format = "JSON";
            chiller.telemetry.pressureSchema.messageSchema.fields = "{\"pressure\":\"Double\",\"pressure_unit\":\"Text\"}";

            sendChillerReportedProperties(&chiller);

            (void)IoTHubDeviceClient_SetDeviceMethodCallback(device_handle, device_method_callback, &chiller);

            while (1)
            {
                temperature = minTemperature + ((double)(rand() % 10) + 5);
                pressure = minPressure + ((double)(rand() % 10) + 5);
                humidity = minHumidity + ((double)(rand() % 20) + 5);

                if (chiller.firmwareUpdateStatus == IDLE)
                {
                    (void)printf("Sending sensor value Temperature = %f %s,\r\n", temperature, "F");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"temperature\":%.2f,\"temperature_unit\":\"F\"}", temperature);
                    send_message(device_handle, msgText, chiller.telemetry.temperatureSchema.messageSchema.name);


                    (void)printf("Sending sensor value Pressure = %f %s,\r\n", pressure, "psig");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"pressure\":%.2f,\"pressure_unit\":\"psig\"}", pressure);
                    send_message(device_handle, msgText, chiller.telemetry.pressureSchema.messageSchema.name);


                    (void)printf("Sending sensor value Humidity = %f %s,\r\n", humidity, "%");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"humidity\":%.2f,\"humidity_unit\":\"%%\"}", humidity);
                    send_message(device_handle, msgText, chiller.telemetry.humiditySchema.messageSchema.name);
                }

                ThreadAPI_Sleep(5000);
            }

            (void)printf("\r\nShutting down\r\n");

            // Clean up the iothub sdk handle and free resources
            IoTHubDeviceClient_Destroy(device_handle);
            free(chiller.firmware);
            free(chiller.new_firmware_URI);
            free(chiller.new_firmware_version);
        }
    }
    // Shutdown the sdk subsystem
    IoTHub_Deinit();

    return 0;
}

Génération et exécution de l’application

Les étapes suivantes décrivent comment utiliser CMake pour générer votre application cliente. L’application cliente de supervision à distance est générée dans le cadre du processus de génération du kit de développement logiciel (SDK).

  1. Dans le fichier remote_monitoring.c, remplacez <connectionstring> par la chaîne de connexion d’appareil que vous avez notée au début de ce guide pratique lorsque vous avez ajouté un appareil à l’accélérateur de solution.

  2. Accédez à la racine de votre copie clonée du référentiel de kits de développement logiciel (SDK) Azure IoT C et exécutez les commandes suivantes pour générer l’application cliente :

    mkdir cmake
    cd cmake
    cmake ../
    make
    
  3. Exécutez l’application cliente et envoyez les données de télémétrie à IoT Hub :

    ./samples/solutions/remote_monitoring_client/remote_monitoring_client
    

    La console affiche des messages quand l’application :

    • L’application envoie un exemple de données de télémétrie à l’accélérateur de solution.
    • Répond aux méthodes appelées à partir du tableau de bord des solutions.

Afficher la télémétrie d’appareil

Vous pouvez afficher les données de télémétrie envoyées à partir de votre périphérique sur la page Explorateur d’appareils dans la solution.

  1. Sélectionnez le périphérique que vous avez configuré dans la liste des périphériques sur la page Explorateur d’appareils. Un panneau affiche des informations sur votre périphérique, y compris un tracé de la télémétrie du périphérique :

    Consultez les détails du périphérique

  2. Choisissez Pression pour modifier l’affichage des données de télémétrie :

    Télémétrie les données de télémétrie de la pression

  3. Pour afficher les informations de diagnostic relatives à votre périphérique, faites défiler la liste jusqu'à Diagnostics :

    Afficher les diagnostics du périphérique

Agir sur votre périphérique

Pour appeler des méthodes sur vos appareils, utilisez la page Explorateur d’appareils dans la solution de supervision à distance. Par exemple, dans la solution de supervision à distance les périphériques Condenseur mettent en œuvre une méthode Redémarrer.

  1. Choisissez Appareils pour accéder à la page Explorateur d’appareils dans la solution.

  2. Sélectionnez le périphérique que vous avez configuré dans la liste des périphériques sur la page Explorateur d’appareils :

    Sélectionner votre appareil réel

  3. Pour afficher une liste des méthodes que vous pouvez appeler sur votre appareil, choisissez Travaux, puis Méthodes. Pour planifier un travail à exécuter sur plusieurs appareils, vous pouvez sélectionner plusieurs appareils dans la liste. Le volet Travaux affiche les types de méthodes communes à tous les appareils que vous avez sélectionnés.

  4. Choisissez Redémarrer, définissez le nom de la tâche RebootPhysicalChiller, puis choisissez Appliquer :

    Planifier la mise à jour du microprogramme

  5. Une série de messages s’affiche dans la console exécutant votre code de périphérique lorsque le périphérique simulé traite la méthode.

Notes

Pour suivre l’état de la tâche dans la solution, choisissez Afficher l’état de la tâche.

Étapes suivantes

L’article Personnaliser l’accélérateur de la solution de supervision à distance décrit quelques méthodes permettant de personnaliser l’accélérateur de solution.