Uw apparaat verbinden met de accelerator voor de externe bewakingsoplossing (Linux)

In deze zelfstudie implementeert u een Chiller-apparaat waarmee de volgende telemetrie wordt verzonden naar de oplossingsversneller Voor externe bewaking:

  • Temperatuur
  • Druk
  • Vochtigheid

Ter vereenvoudiging genereert de code voorbeeldtelemetriewaarden voor de chiller. U kunt het voorbeeld uitbreiden door echte sensoren te verbinden met uw apparaat en echte telemetrie te verzenden.

Het voorbeeldapparaat ook:

  • Hiermee worden metagegevens naar de oplossing verzonden om de mogelijkheden ervan te beschrijven.
  • Reageert op acties die zijn geactiveerd vanaf de pagina Apparaten in de oplossing.
  • Reageert op configuratiewijzigingen die worden verzonden vanaf de pagina Apparaten in de oplossing.

U hebt een actief Azure-account nodig om deze zelfstudie te voltooien. Als u geen account hebt, kunt u binnen een paar minuten een gratis proefaccount maken. Zie Gratis proefversie van Azure voor meer informatie.

Voordat u begint

Voordat u code schrijft voor uw apparaat, implementeert u de oplossingsversneller voor externe bewaking en voegt u een nieuw echt apparaat toe aan de oplossing.

De oplossingsversneller voor externe bewaking implementeren

Het Chiller-apparaat dat u in deze zelfstudie maakt, verzendt gegevens naar een exemplaar van de oplossingsversneller Voor externe bewaking . Als u de oplossingsversneller Voor externe bewaking nog niet hebt ingericht in uw Azure-account, raadpleegt u De oplossingsversneller voor externe bewaking implementeren

Wanneer het implementatieproces voor de externe bewakingsoplossing is voltooid, klikt u op Starten om het oplossingsdashboard in uw browser te openen.

Het oplossingsdashboard

Uw apparaat toevoegen aan de externe bewakingsoplossing

Notitie

Als u al een apparaat in uw oplossing hebt toegevoegd, kunt u deze stap overslaan. De volgende stap vereist echter dat uw apparaat connection string. U kunt de connection string van een apparaat ophalen uit de Azure Portal of met behulp van het hulpprogramma az iot CLI.

Als een apparaat verbinding kan maken met de oplossingsversneller, moet het zichzelf identificeren om te IoT Hub met geldige referenties. U hebt de mogelijkheid om het apparaat op te slaan connection string die deze referenties bevat wanneer u het apparaat aan de oplossing toevoegt. Verderop in deze zelfstudie neemt u het apparaat connection string op in uw clienttoepassing.

Als u een apparaat wilt toevoegen aan uw externe bewakingsoplossing, voert u de volgende stappen uit op de pagina Device Explorer in de oplossing:

  1. Kies + Nieuw apparaat en kies vervolgens Real als het apparaattype:

    Echt apparaat toevoegen

  2. Voer de fysieke koelunit in als apparaat-id . Kies de opties voor symmetrische sleutels en automatisch genereren :

    Apparaatopties kiezen

  3. Kies Toepassen. Noteer vervolgens de waarden voor de primaire sleutel van de apparaat-id, primaire sleutel en verbindingsreeks :

    Referenties ophalen

U hebt nu een echt apparaat toegevoegd aan de accelerator voor de externe bewakingsoplossing en het apparaat connection string genoteerd. In de volgende secties implementeert u de clienttoepassing die gebruikmaakt van het apparaat connection string om verbinding te maken met uw oplossing.

De clienttoepassing implementeert het ingebouwde Chiller-apparaatmodel . Een apparaatmodel voor een oplossingsversneller geeft het volgende over een apparaat op:

  • De eigenschappen die het apparaat aan de oplossing rapporteert. Een Chiller-apparaat rapporteert bijvoorbeeld informatie over de firmware en locatie.
  • De typen telemetrie die het apparaat verzendt naar de oplossing. Een Chiller-apparaat verzendt bijvoorbeeld temperatuur-, vochtigheids- en drukwaarden.
  • De methoden die u kunt plannen vanuit de oplossing om op het apparaat uit te voeren. Een Chiller-apparaat moet bijvoorbeeld de methoden Reboot, FirmwareUpdate, EmergencyValveRelease en IncreasePressure implementeren.

In deze zelfstudie leert u hoe u een echt apparaat verbindt met de oplossingsversneller voor externe bewaking.

Net als bij de meeste ingesloten toepassingen die worden uitgevoerd op beperkte apparaten, wordt de clientcode voor de apparaattoepassing geschreven in C. In deze zelfstudie bouwt u de toepassing op een computer met Ubuntu (Linux).

Zie Een nieuw gesimuleerd apparaat maken en testen als u liever een apparaat simuleert.

Vereisten

Als u de stappen in deze handleiding wilt voltooien, hebt u een apparaat met Ubuntu versie 15.04 of hoger nodig. Voordat u doorgaat, stelt u uw Linux-ontwikkelomgeving in.

De code weergeven

De voorbeeldcode die in deze handleiding wordt gebruikt, is beschikbaar in de GitHub-opslagplaats van Azure IoT C SDK's.

De broncode downloaden en het project voorbereiden

Als u het project wilt voorbereiden, kloont of downloadt u de Azure IoT C SDK-opslagplaats vanuit GitHub.

Het voorbeeld bevindt zich in de map samples/solutions/remote_monitoring_client .

Open het bestand remote_monitoring.c in de map samples/solutions/remote_monitoring_client in een teksteditor.

Kennismaking met code

In deze sectie worden enkele van de belangrijkste onderdelen van de voorbeeldcode beschreven en wordt uitgelegd hoe deze zich verhouden tot de oplossingsversneller Voor externe bewaking.

In het volgende codefragment ziet u hoe de gerapporteerde eigenschappen die de mogelijkheden van het apparaat beschrijven, worden gedefinieerd. Deze eigenschappen zijn onder andere:

  • De locatie van het apparaat om de oplossingsversneller in te schakelen om het apparaat toe te voegen aan de kaart.
  • De huidige firmwareversie.
  • De lijst met methoden die het apparaat ondersteunt.
  • Het schema van de telemetrieberichten die door het apparaat worden verzonden.
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;

Het voorbeeld bevat een serializeToJson-functie waarmee deze gegevensstructuur wordt geserialiseerd met behulp van de Parson-bibliotheek.

Het voorbeeld bevat verschillende callbackfuncties waarmee informatie naar de console wordt afgedrukt wanneer de client communiceert met de oplossingsversneller:

  • connection_status_callback
  • send_confirm_callback
  • reported_state_callback
  • device_method_callback

In het volgende codefragment ziet u de functie device_method_callback . Deze functie bepaalt de actie die moet worden uitgevoerd wanneer een methode-aanroep wordt ontvangen van de oplossingsversneller. De functie ontvangt een verwijzing naar de chiller-gegevensstructuur in de parameter userContextCallback . De waarde van userContextCallback wordt ingesteld wanneer de callback-functie is geconfigureerd in de hoofdfunctie :

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

Wanneer de oplossingsversneller de firmware-updatemethode aanroept, wordt de JSON-nettolading gedeserialiseerd en wordt een achtergrondthread gestart om het updateproces te voltooien. Het volgende codefragment toont de do_firmware_update die op de thread wordt uitgevoerd:

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

In het volgende codefragment ziet u hoe de client een telemetriebericht verzendt naar de oplossingsversneller. De berichteigenschappen bevatten het berichtschema om de oplossingsversneller te helpen de telemetrie op het dashboard weer te geven:

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

De hoofdfunctie in het voorbeeld:

  • Initialiseert en sluit het SDK-subsysteem af.
  • Initialiseert de gegevensstructuur van de chiller .
  • Hiermee worden de gerapporteerde eigenschappen naar de oplossingsversneller verzonden.
  • Hiermee configureert u de callbackfunctie van de apparaatmethode.
  • Hiermee worden gesimuleerde telemetriewaarden naar de oplossingsversneller verzonden.
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;
}

De toepassing bouwen en uitvoeren.

In de volgende stappen wordt beschreven hoe u CMake gebruikt om de clienttoepassing te bouwen. De clienttoepassing voor externe bewaking wordt gebouwd als onderdeel van het buildproces voor de SDK.

  1. Bewerk het bestand remote_monitoring.c om te vervangen <connectionstring> door het apparaat connection string u aan het begin van deze instructies hebt genoteerd wanneer u een apparaat aan de oplossingsversneller hebt toegevoegd.

  2. Navigeer naar de hoofdmap van de gekloonde kopie van de opslagplaats voor Azure IoT C SDK's en voer de volgende opdrachten uit om de clienttoepassing te bouwen:

    mkdir cmake
    cd cmake
    cmake ../
    make
    
  3. Voer de clienttoepassing uit en verzend telemetrie naar IoT Hub:

    ./samples/solutions/remote_monitoring_client/remote_monitoring_client
    

    De console geeft berichten weer als:

    • De toepassing verzendt voorbeeldtelemetrie naar de oplossingsversneller.
    • Reageert op methoden die worden aangeroepen vanuit het oplossingsdashboard.

Telemetrie van apparaten weergeven

U kunt de telemetrie bekijken die vanaf uw apparaat is verzonden op de pagina Device Explorer in de oplossing.

  1. Selecteer het apparaat dat u hebt ingericht in de lijst met apparaten op de pagina Device Explorer . In een deelvenster wordt informatie weergegeven over uw apparaat, inclusief een plot van de telemetrie van het apparaat:

    Apparaatdetails weergeven

  2. Kies Druk om de telemetrieweergave te wijzigen:

    Telemetrie van druk weergeven

  3. Als u diagnostische informatie over uw apparaat wilt weergeven, schuift u omlaag naar Diagnostische gegevens:

    Apparaatdiagnose weergeven

Reageren op uw apparaat

Als u methoden op uw apparaten wilt aanroepen, gebruikt u de pagina Device Explorer in de externe bewakingsoplossing. Implementeer bijvoorbeeld in de Chiller-apparaten van de externe bewakingsoplossing een methode Voor opnieuw opstarten .

  1. Kies Apparaten om naar de pagina Device Explorer in de oplossing te navigeren.

  2. Selecteer het apparaat dat u hebt ingericht in de lijst met apparaten op de pagina Device Explorer :

    Uw echte apparaat selecteren

  3. Als u een lijst met de methoden wilt weergeven die u op uw apparaat kunt aanroepen, kiest u Taken en vervolgens Methoden. Als u wilt plannen dat een taak op meerdere apparaten wordt uitgevoerd, kunt u meerdere apparaten in de lijst selecteren. In het deelvenster Taken ziet u de typen methoden die gebruikelijk zijn voor alle apparaten die u hebt geselecteerd.

  4. Kies Opnieuw opstarten, stel de taaknaam in op RebootPhysicalChiller en kies Vervolgens Toepassen:

    De firmware-update plannen

  5. Een reeks berichten wordt weergegeven in de console waarop uw apparaatcode wordt uitgevoerd terwijl het gesimuleerde apparaat de methode verwerkt.

Notitie

Als u de status van de taak in de oplossing wilt bijhouden, kiest u Taakstatus weergeven.

Volgende stappen

In het artikel De accelerator voor de externe bewakingsoplossing aanpassen worden enkele manieren beschreven om de oplossingsversneller aan te passen.