Ansluta enheten till acceleratorn fjärrövervakningslösning (Windows)

I den här självstudien implementerar du en kylaggregatsenhet som skickar följande telemetri till acceleratorn Fjärrövervakningslösning:

  • Temperatur
  • Lufttryck
  • Luftfuktighet

För enkelhetens skull genererar koden exempeltelemetrivärden för kylaggregatet. Du kan utöka exemplet genom att ansluta verkliga sensorer till din enhet och skicka verklig telemetri.

Exempelenheten:

  • Skickar metadata till lösningen för att beskriva dess funktioner.
  • Svarar på åtgärder som utlöses från sidan Enheter i lösningen.
  • Svarar på konfigurationsändringar som skickas från sidan Enheter i lösningen.

Du behöver ett Azure-konto för att slutföra den här självstudiekursen. Om du inte har något konto kan skapa du ett kostnadsfritt utvärderingskonto på bara några minuter. Mer information om den kostnadsfria utvärderingsversionen av Azure finns Kostnadsfri utvärderingsversion av Azure.

Innan du börjar

Innan du skriver kod för enheten distribuerar du acceleratorn Fjärrövervakningslösning och lägger till en ny verklig enhet i lösningen.

Distribuera acceleratorn för fjärrövervakningslösningen

Kylaggregatet som du skapar i den här självstudien skickar data till en instans av acceleratorn Fjärrövervakningslösning. Om du inte redan har etablerat acceleratorn för fjärrövervakningslösningen i ditt Azure-konto kan du läsa Distribuera acceleratorn fjärrövervakningslösning

När distributionsprocessen för fjärrövervakningslösningen är klar klickar du på Starta för att öppna lösningens instrumentpanel i webbläsaren.

Instrumentpanelen för lösningen

Lägga till enheten i fjärrövervakningslösningen

Anteckning

Om du redan har lagt till en enhet i din lösning kan du hoppa över det här steget. Nästa steg kräver dock enhetens anslutningssträng. Du kan hämta en enhets anslutningssträng från Azure Portal eller med hjälp av cli-verktyget az iot.

För att en enhet ska kunna ansluta till lösningsacceleratorn måste den identifiera sig för att IoT Hub med giltiga autentiseringsuppgifter. Du har möjlighet att spara enhetsanslutningssträngen som innehåller dessa autentiseringsuppgifter när du lägger till enheten i lösningen. Du inkluderar enhetens anslutningssträng i klientprogrammet senare i den här självstudien.

Om du vill lägga till en enhet i fjärrövervakningslösningen utför du följande steg på sidan Device Explorer i lösningen:

  1. Välj + Ny enhet och välj sedan Verklig som Enhetstyp:

    Lägga till en riktig enhet

  2. Ange Fysisk kylaggregat som Enhets-ID . Välj alternativen Symmetrisk nyckel och Generera nycklar automatiskt :

    Välj enhetsalternativ

  3. Välj Använd. Anteckna sedan värdena för Enhets-ID, Primärnyckel och Primärnyckel för anslutningssträng :

    Hämta autentiseringsuppgifter

Nu har du lagt till en riktig enhet i acceleratorn fjärrövervakningslösning och antecknat dess enhetsanslutningssträng. I följande avsnitt implementerar du klientprogrammet som använder enhetens anslutningssträng för att ansluta till din lösning.

Klientprogrammet implementerar den inbyggda kylaggregatsmodellen . En enhetsmodell för lösningsacceleratorn anger följande om en enhet:

  • Egenskaperna som enheten rapporterar till lösningen. En Kylaggregat-enhet rapporterar till exempel information om dess inbyggda programvara och plats.
  • De typer av telemetri som enheten skickar till lösningen. En kylaggregat skickar till exempel värden för temperatur, luftfuktighet och tryck.
  • De metoder som du kan schemalägga från lösningen för att köras på enheten. En kylaggregatenhet måste till exempel implementera metoderna Reboot, FirmwareUpdate, EmergencyValveRelease och IncreasePressure .

Den här självstudien visar hur du ansluter en riktig enhet till acceleratorn Fjärrövervakningslösning.

Precis som med de flesta inbäddade program som körs på begränsade enheter skrivs klientkoden för enhetsprogrammet i C. I den här självstudien skapar du enhetsklientprogrammet på en dator som kör Windows.

Om du föredrar att simulera en enhet kan du läsa Skapa och testa en ny simulerad enhet.

Förutsättningar

För att slutföra stegen i den här guiden följer du stegen i konfigurera din Windows-utvecklingsmiljö för att lägga till nödvändiga utvecklingsverktyg och bibliotek på din Windows-dator.

Visa koden

Exempelkoden som används i den här guiden finns på GitHub-lagringsplatsen för Azure IoT C SDK:er.

Ladda ned källkoden och förbered projektet

Förbered projektet genom att klona Azure IoT C SDK:er från GitHub.

Exemplet finns i mappen samples/solutions/remote_monitoring_client .

Öppna filen remote_monitoring.c i mappen samples/solutions/remote_monitoring_client i en textredigerare.

Steg-för-steg-beskrivning av kod

Det här avsnittet beskriver några av de viktigaste delarna i exempelkoden och förklarar hur de relaterar till acceleratorn fjärrövervakningslösning.

Följande kodfragment visar hur de rapporterade egenskaperna som beskriver enhetens funktioner definieras. Dessa egenskaper omfattar:

  • Platsen för enheten för att aktivera lösningsacceleratorn för att lägga till enheten på kartan.
  • Den aktuella versionen av inbyggd programvara.
  • Listan över metoder som enheten stöder.
  • Schemat för telemetrimeddelanden som skickas av enheten.
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;

Exemplet innehåller en serializeToJson-funktion som serialiserar datastrukturen med hjälp av Parson-biblioteket.

Exemplet innehåller flera återanropsfunktioner som skriver ut information till konsolen när klienten interagerar med lösningsacceleratorn:

  • connection_status_callback
  • send_confirm_callback
  • reported_state_callback
  • device_method_callback

Följande kodfragment visar funktionen device_method_callback . Den här funktionen avgör vilken åtgärd som ska vidtas när ett metodanrop tas emot från lösningsacceleratorn. Funktionen tar emot en referens till chiller-datastrukturen i parametern userContextCallback . Värdet för userContextCallback anges när återanropsfunktionen har konfigurerats i huvudfunktionen :

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

När lösningsacceleratorn anropar uppdateringsmetoden för inbyggd programvara deserialiserar exemplet JSON-nyttolasten och startar en bakgrundstråd för att slutföra uppdateringsprocessen. Följande kodfragment visar do_firmware_update som körs på tråden:

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

Följande kodfragment visar hur klienten skickar ett telemetrimeddelande till lösningsacceleratorn. Meddelandeegenskaperna innehåller meddelandeschemat som hjälper lösningsacceleratorn att visa telemetrin på instrumentpanelen:

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

Huvudfunktionen i exemplet:

  • Initierar och stänger av SDK-undersystemet.
  • Initierar kylaggregatets datastruktur.
  • Skickar de rapporterade egenskaperna till lösningsacceleratorn.
  • Konfigurerar återanropsfunktionen för enhetsmetod.
  • Skickar simulerade telemetrivärden till lösningsacceleratorn.
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;
}

Skapa och köra exempelappen

  1. Redigera filen remote_monitoring.c för att ersätta <connectionstring> med enhetsanslutningssträngen som du antecknade i början av den här guiden när du lade till en enhet i lösningsacceleratorn.

  2. Följ stegen i Skapa C SDK i Windows för att skapa SDK och klientprogrammet för fjärrövervakning.

  3. I kommandotolken som du använde för att skapa lösningen kör du:

    samples\solutions\remote_monitoring_client\Release\remote_monitoring_client.exe
    

    Konsolen visar meddelanden som:

    • Programmet skickar exempeltelemetri till lösningsacceleratorn.
    • Svarar på metoder som anropas från lösningens instrumentpanel.

Visa enhetstelemetri

Du kan visa telemetrin som skickas från enheten på sidan Device Explorer i lösningen.

  1. Välj den enhet som du etablerade i listan över enheter på sidan Device Explorer . En panel visar information om din enhet, inklusive ett diagram över enhetens telemetri:

    Se enhetsinformation

  2. Välj Tryck för att ändra telemetrivisningen:

    Visa trycktelemetri

  3. Om du vill visa diagnostikinformation om din enhet rullar du ned till Diagnostik:

    Visa enhetsdiagnostik

Agera på din enhet

Om du vill anropa metoder på dina enheter använder du sidan Device Explorer i fjärrövervakningslösningen. I fjärrövervakningslösningen implementerar till exempel kylaggregatet en omstartsmetod .

  1. Välj Enheter för att gå till sidan Device Explorer i lösningen.

  2. Välj den enhet som du etablerade i listan över enheter på sidan Device Explorer :

    Välj din riktiga enhet

  3. Om du vill visa en lista över de metoder som du kan anropa på enheten väljer du Jobb och sedan Metoder. Om du vill schemalägga ett jobb som ska köras på flera enheter kan du välja flera enheter i listan. Panelen Jobb visar de metodtyper som är gemensamma för alla enheter som du har valt.

  4. Välj Starta om, ange jobbnamnet till RebootPhysicalChiller och välj sedan Använd:

    Schemalägg uppdateringen av den inbyggda programvaran

  5. En sekvens med meddelanden visas i konsolen som kör enhetskoden medan den simulerade enheten hanterar metoden.

Anteckning

Om du vill spåra status för jobbet i lösningen väljer du Visa jobbstatus.

Nästa steg

I artikeln Anpassa acceleratorn fjärrövervakningslösning beskrivs några sätt att anpassa lösningsacceleratorn.