Zelfstudie: Een clienttoepassing maken en verbinden met uw Azure IoT Central-toepassing

In deze zelfstudie leert u hoe u een clienttoepassing verbindt met uw Azure IoT Central-toepassing. De toepassing simuleert het gedrag van een temperatuurcontrollerapparaat. Wanneer de toepassing verbinding maakt met IoT Central, wordt de model-id van het apparaatmodel van de temperatuurcontroller verzonden. IoT Central gebruikt de model-id om het apparaatmodel op te halen en een sjabloon voor u te maken. U voegt weergaven toe aan de apparaatsjabloon zodat een operator met een apparaat kan communiceren.

In deze zelfstudie leert u het volgende:

  • De apparaatcode maken, deze uitvoeren en kijken hoe deze verbinding maakt met uw IoT Central-toepassing.
  • De gesimuleerde telemetrie bekijken die vanaf het apparaat wordt verzonden.
  • Aangepaste weergaven toevoegen aan een apparaatsjabloon.
  • De sjabloon van het apparaat publiceren.
  • Een weergave gebruiken om apparaateigenschappen te beheren.
  • Een opdracht aanroepen om het apparaat te beheren.

Browse code

Vereisten

Voor het voltooien van de stappen in deze zelfstudie hebt u het volgende nodig:

U kunt deze zelfstudie uitvoeren in Linux of Windows. De shell-opdrachten in deze zelfstudie volgen de Linux-conventie voor padscheidingstekens /. Als u de stappen uitvoert in Windows, moet u deze scheidingstekens vervangen door \.

De vereisten verschillen per besturingssysteem:

Linux

In deze zelfstudie wordt ervan uitgegaan dat u Ubuntu Linux gebruikt. De stappen in deze zelfstudie zijn getest met Ubuntu 18.04.

Om deze zelfstudie in Linux te voltooien installeert u de volgende software in uw lokale Linux-omgeving:

Installeer GCC, Git, cmakeen alle vereiste afhankelijkheden met behulp van de opdracht apt-get:

sudo apt-get update
sudo apt-get install -y git cmake build-essential curl libcurl4-openssl-dev libssl-dev uuid-dev

Controleer of de versie cmake groter is dan 2.8.12 en of de versie van GCC groter is dan 4.4.7.

cmake --version
gcc --version

Vensters

Om deze zelfstudie in Windows te voltooien, installeert u de volgende software in uw lokale Windows-omgeving:

De code downloaden

In deze zelfstudie bereidt u een ontwikkelomgeving voor die kan worden gebruikt voor het klonen en compileren van de Azure IoT Hub Device C-SDK.

Open een opdrachtprompt in de map van uw keuze. Voer de volgende opdracht uit om de GitHub-opslagplaats Azure IoT C-SDK’s en -bibliotheken te klonen op deze locatie:

git clone https://github.com/Azure/azure-iot-sdk-c.git
cd azure-iot-sdk-c
git submodule update --init

Deze bewerking kan enkele minuten in beslag nemen.

De code bekijken

Open in de kopie van de Microsoft Azure IoT SDK voor C die u eerder hebt gedownload de bestanden azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c en azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c in een teksteditor.

In het voorbeeld wordt het digital twin definition Language-model met meerdere onderdelen geïmplementeerd.

Wanneer u het voorbeeld uitvoert om verbinding te maken met IoT Central, wordt daarvoor gebruikgemaakt van de Device Provisioning Service (DPS) om het apparaat te registreren en een verbindingsreeks te genereren. Het voorbeeld haalt de DPS-verbindingsgegevens die nodig zijn op uit de opdrachtregelomgeving.

In pnp_temperature_controller.c roept CreateDeviceClientAndAllocateComponents de main functie eerst het volgende aan:

  • De model-id dtmi:com:example:Thermostat;1 in te stellen. IoT Central gebruikt de model-id om de apparaatsjabloon voor dit apparaat te identificeren of te genereren. Zie Een apparaat toewijzen aan een apparaatsjabloon voor meer informatie.
  • DPS te gebruiken om het apparaat in te richten en te registreren.
  • Een clientingang voor het apparaat te maken en verbinding te maken met uw IoT Central-toepassing.
  • Hiermee maakt u een handler voor opdrachten in het temperatuurcontrolleronderdeel.
  • Hiermee maakt u een handler voor eigenschapsupdates in het onderdeel temperatuurcontroller.
  • Hiermee worden de twee thermostaatonderdelen gemaakt.

De main volgende functie:

  • Rapporteert enkele initiële eigenschapswaarden voor alle onderdelen.
  • Hiermee wordt een lus gestart om telemetrie van alle onderdelen te verzenden.

De functie main start vervolgens een thread voor het periodiek verzenden van telemetrie.

int main(void)
{
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient = NULL;

    g_pnpDeviceConfiguration.modelId = g_temperatureControllerModelId;
    g_pnpDeviceConfiguration.enableTracing = g_hubClientTraceEnabled;

    // First determine the IoT Hub / credentials / device to use.
    if (GetConnectionSettingsFromEnvironment(&g_pnpDeviceConfiguration) == false)
    {
        LogError("Cannot read required environment variable(s)");
    }
    // Creates the thermostat subcomponents defined by this model.  Since everything
    // is simulated, this setup stage just creates simulated objects in memory.
    else if (AllocateThermostatComponents() == false)
    {
        LogError("Failure allocating thermostat components");
    }
    // Create a handle to device client handle.  Note that this call may block
    // for extended periods of time when using DPS.
    else if ((deviceClient = CreateAndConfigureDeviceClientHandleForPnP()) == NULL)
    {
        LogError("Failure creating Iot Hub device client");
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);
    }
    else
    {
        LogInfo("Successfully created device client.  Hit Control-C to exit program\n");

        int numberOfIterations = 0;

        // During startup, send what DTDLv2 calls "read-only properties" to indicate initial device state.
        PnP_TempControlComponent_ReportSerialNumber_Property(deviceClient);
        PnP_DeviceInfoComponent_Report_All_Properties(g_deviceInfoComponentName, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle1, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle2, deviceClient);

        while (true)
        {
            // Wake up periodically to poll.  Even if we do not plan on sending telemetry, we still need to poll periodically in order to process
            // incoming requests from the server and to do connection keep alives.
            if ((numberOfIterations % g_sendTelemetryPollInterval) == 0)
            {
                PnP_TempControlComponent_SendWorkingSet(deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle1, deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle2, deviceClient);
            }

            IoTHubDeviceClient_LL_DoWork(deviceClient);
            ThreadAPI_Sleep(g_sleepBetweenPollsMs);
            numberOfIterations++;
        }

        // The remainder of the code is used for cleaning up our allocated resources. It won't be executed in this 
        // sample (because the loop above is infinite and is only broken out of by Control-C of the program), but 
        // it is included for reference.

        // Free the memory allocated to track simulated thermostat.
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);

        // Clean up the IoT Hub SDK handle.
        IoTHubDeviceClient_LL_Destroy(deviceClient);
        // Free all IoT Hub subsystem.
        IoTHub_Deinit();
    }

    return 0;
}

In pnp_thermostat_component.cde PnP_ThermostatComponent_SendCurrentTemperature functie ziet u hoe het apparaat de temperatuurtelemetrie verzendt van een onderdeel naar IoT Central:

void PnP_ThermostatComponent_SendCurrentTemperature(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    IOTHUB_MESSAGE_HANDLE messageHandle = NULL;
    IOTHUB_MESSAGE_RESULT messageResult;
    IOTHUB_CLIENT_RESULT iothubClientResult;

    char temperatureStringBuffer[CURRENT_TEMPERATURE_BUFFER_SIZE];

    // Create the telemetry message body to send.
    if (snprintf(temperatureStringBuffer, sizeof(temperatureStringBuffer), g_temperatureTelemetryBodyFormat, pnpThermostatComponent->currentTemperature) < 0)
    {
        LogError("snprintf of current temperature telemetry failed");
    }
    // Create the message handle and specify its metadata.
    else if ((messageHandle = IoTHubMessage_CreateFromString(temperatureStringBuffer)) == NULL)
    {
        LogError("IoTHubMessage_PnP_CreateFromString failed");
    }
    else if ((messageResult = IoTHubMessage_SetContentTypeSystemProperty(messageHandle, g_jsonContentType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentTypeSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetContentEncodingSystemProperty(messageHandle, g_utf8EncodingType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetComponentName(messageHandle, pnpThermostatComponent->componentName)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    // Send the telemetry message.
    else if ((iothubClientResult = IoTHubDeviceClient_LL_SendTelemetryAsync(deviceClient, messageHandle, NULL, NULL)) != IOTHUB_CLIENT_OK)
    {
        LogError("Unable to send telemetry message, error=%d", iothubClientResult);
    }

    IoTHubMessage_Destroy(messageHandle);
}

In pnp_thermostat_component.cverzendt de PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property functie een eigenschapsupdate maxTempSinceLastReboot van het onderdeel naar IoT Central:

void PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    char maximumTemperatureAsString[MAX_TEMPERATURE_SINCE_REBOOT_BUFFER_SIZE];
    IOTHUB_CLIENT_RESULT iothubClientResult;

    if (snprintf(maximumTemperatureAsString, sizeof(maximumTemperatureAsString), g_maxTempSinceLastRebootPropertyFormat, pnpThermostatComponent->maxTemperature) < 0)
    {
        LogError("Unable to create max temp since last reboot string for reporting result");
    }
    else
    {
        IOTHUB_CLIENT_PROPERTY_REPORTED maxTempProperty;
        maxTempProperty.structVersion = IOTHUB_CLIENT_PROPERTY_REPORTED_STRUCT_VERSION_1;
        maxTempProperty.name = g_maxTempSinceLastRebootPropertyName;
        maxTempProperty.value =  maximumTemperatureAsString;

        unsigned char* propertySerialized = NULL;
        size_t propertySerializedLength;

        // The first step of reporting properties is to serialize IOTHUB_CLIENT_PROPERTY_WRITABLE_RESPONSE into JSON for sending.
        if ((iothubClientResult = IoTHubClient_Properties_Serializer_CreateReported(&maxTempProperty, 1, pnpThermostatComponent->componentName, &propertySerialized, &propertySerializedLength)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to serialize reported state, error=%d", iothubClientResult);
        }
        // The output of IoTHubClient_Properties_Serializer_CreateReported is sent to IoTHubDeviceClient_LL_SendPropertiesAsync to perform network I/O.
        else if ((iothubClientResult = IoTHubDeviceClient_LL_SendPropertiesAsync(deviceClient, propertySerialized, propertySerializedLength,  NULL, NULL)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to send reported state, error=%d", iothubClientResult);
        }
        else
        {
            LogInfo("Sending %s property to IoTHub for component %s", g_maxTempSinceLastRebootPropertyName, pnpThermostatComponent->componentName);
        }
        IoTHubClient_Properties_Serializer_Destroy(propertySerialized);
    }
}

In pnp_thermostat_component.cde PnP_ThermostatComponent_ProcessPropertyUpdate functie worden schrijfbare eigenschapsupdates van IoT Central verwerkt:

void PnP_ThermostatComponent_ProcessPropertyUpdate(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient, const char* propertyName, const char* propertyValue, int version)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;

    if (strcmp(propertyName, g_targetTemperaturePropertyName) != 0)
    {
        LogError("Property %s was requested to be changed but is not part of the thermostat interface definition", propertyName);
    }
    else
    {
        char* next;
        double targetTemperature = strtod(propertyValue, &next);
        if ((propertyValue == next) || (targetTemperature == HUGE_VAL) || (targetTemperature == (-1*HUGE_VAL)))
        {
            LogError("Property %s is not a valid number", propertyValue);
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_BAD_FORMAT, version, g_temperaturePropertyResponseDescriptionNotInt);
        }
        else
        {
            LogInfo("Received targetTemperature %f for component %s", targetTemperature, pnpThermostatComponent->componentName);
            
            bool maxTempUpdated = false;
            UpdateTemperatureAndStatistics(pnpThermostatComponent, targetTemperature, &maxTempUpdated);

            // The device needs to let the service know that it has received the targetTemperature desired property.
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_SUCCESS, version, NULL);
            
            if (maxTempUpdated)
            {
                // If the maximum temperature has been updated, we also report this as a property.
                PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(pnpThermostatComponent, deviceClient);
            }
        }
    }
}

In pnp_thermostat_component.c, de PnP_ThermostatComponent_ProcessCommand functie verwerkt opdrachten aangeroepen vanuit IoT Central:

void PnP_ThermostatComponent_ProcessCommand(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, const char *pnpCommandName, JSON_Value* commandJsonValue, IOTHUB_CLIENT_COMMAND_RESPONSE* commandResponse)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    const char* sinceStr;

    if (strcmp(pnpCommandName, g_getMaxMinReportCommandName) != 0)
    {
        LogError("Command %s is not supported on thermostat component", pnpCommandName);
        commandResponse->statusCode = PNP_STATUS_NOT_FOUND;
    }
    // See caveats section in ../readme.md; we don't actually respect this sinceStr to keep the sample simple,
    // but want to demonstrate how to parse out in any case.
    else if ((sinceStr = json_value_get_string(commandJsonValue)) == NULL)
    {
        LogError("Cannot retrieve JSON string for command");
        commandResponse->statusCode = PNP_STATUS_BAD_FORMAT;
    }
    else if (BuildMaxMinCommandResponse(pnpThermostatComponent, commandResponse) == false)
    {
        LogError("Unable to build response for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_INTERNAL_ERROR;
    }
    else
    {
        LogInfo("Returning success from command request for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_SUCCESS;
    }
}

De code bouwen

U gebruikt de SDK van het apparaat om de opgenomen voorbeeldcode te maken:

  1. Maak een submap cmake in de hoofdmap van de device-SDK en navigeer naar die map:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Voer de volgende opdrachten uit om de SDK en voorbeelden te genereren:

    cmake -Duse_prov_client=ON -Dhsm_type_symm_key=ON -Drun_e2e_tests=OFF ..
    cmake --build .
    

Verbindingsgegevens ophalen

Wanneer u de toepassing voor het voorbeeldapparaat later in deze zelfstudie uitvoert, hebt u de volgende configuratiewaarden nodig:

  • Id-bereik: Navigeer in uw IoT Central-toepassing naar verbindingsgroepen voor machtigingen>. Noteer de waarde van Id-bereik.
  • Primaire sleutel groeperen: Navigeer in uw IoT Central-toepassing naar Verbindingsgroepen > voor machtigingen > voor SAS-IoT-Devices. Noteer de waarde van Primaire sleutel onder Shared Access Signature (SAS).

Gebruik Azure Cloud Shell om een apparaatsleutel te genereren op basis van de primaire sleutel van de groep die u hebt opgehaald:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Noteer de gegenereerde apparaatsleutel. U hebt deze waarde verderop in deze zelfstudie nodig.

Notitie

Als u dit voorbeeld wilt uitvoeren, hoeft u het apparaat niet vooraf te registreren in uw IoT Central-toepassing. In het voorbeeld wordt de IoT Central-mogelijkheid gebruikt om apparaten automatisch te registreren wanneer ze voor het eerst verbinding maken.

De code uitvoeren

Als u de voorbeeldtoepassing wilt uitvoeren, opent u een opdrachtregelomgeving en navigeert u naar de map azure-iot-sdk-c\cmake.

Stel de omgevingsvariabelen in om het voorbeeld te configureren. Het volgende codefragment laat zien hoe u de omgevingsvariabelen instelt vanaf de Windows-opdrachtprompt. Als u een bash-shell gebruikt, vervangt u de set-opdrachten door export-opdrachten:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Het voorbeeld uitvoeren:

# Bash
cd iothub_client/samples/pnp/pnp_temperature_controller/
./pnp_temperature_controller
REM Windows
cd iothub_client\samples\pnp\pnp_temperature_controller\Debug
.\pnp_temperature_controller.exe

In de volgende uitvoer ziet u hoe het apparaat zich registreert bij IoT Central en er verbinding mee maakt. Het voorbeeld begint met het verzenden van telemetriegegevens:

Info: Initiating DPS client to retrieve IoT Hub connection information
-> 09:43:27 CONNECT | VER: 4 | KEEPALIVE: 0 | FLAGS: 194 | USERNAME: 0ne0026656D/registrations/sample-device-01/api-version=2019-03-31&ClientVersion=1.6.0 | PWD: XXXX | CLEAN: 1
<- 09:43:28 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:29 SUBSCRIBE | PACKET_ID: 1 | TOPIC_NAME: $dps/registrations/res/# | QOS: 1
<- 09:43:30 SUBACK | PACKET_ID: 1 | RETURN_CODE: 1
-> 09:43:30 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/PUT/iotdps-register/?$rid=1 | PAYLOAD_LEN: 102
<- 09:43:31 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=1&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 94
-> 09:43:31 PUBACK | PACKET_ID: 2
-> 09:43:33 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=2&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:34 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=2&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 173
-> 09:43:34 PUBACK | PACKET_ID: 2
-> 09:43:36 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=3&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:37 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/200/?$rid=3 | PACKET_ID: 2 | PAYLOAD_LEN: 478
-> 09:43:37 PUBACK | PACKET_ID: 2
Info: Provisioning callback indicates success.  iothubUri=iotc-60a....azure-devices.net, deviceId=sample-device-01
-> 09:43:37 DISCONNECT
Info: DPS successfully registered.  Continuing on to creation of IoTHub device client handle.
Info: Successfully created device client.  Hit Control-C to exit program

Info: Sending serialNumber property to IoTHub
Info: Sending device information property to IoTHub.  propertyName=swVersion, propertyValue="1.0.0.0"
Info: Sending device information property to IoTHub.  propertyName=manufacturer, propertyValue="Sample-Manufacturer"
Info: Sending device information property to IoTHub.  propertyName=model, propertyValue="sample-Model-123"
Info: Sending device information property to IoTHub.  propertyName=osName, propertyValue="sample-OperatingSystem-name"
Info: Sending device information property to IoTHub.  propertyName=processorArchitecture, propertyValue="Contoso-Arch-64bit"
Info: Sending device information property to IoTHub.  propertyName=processorManufacturer, propertyValue="Processor Manufacturer(TM)"
Info: Sending device information property to IoTHub.  propertyName=totalStorage, propertyValue=10000
Info: Sending device information property to IoTHub.  propertyName=totalMemory, propertyValue=200
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat1
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat2
-> 09:43:44 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: iotc-60a576a2-eec7-48e2-9306-9e7089a79995.azure-devices.net/sample-device-01/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.6.0%20(native%3b%20Linux%3b%20x86_64)&model-id=dtmi%3acom%3aexample%3aTemperatureController%3b1 | PWD: XXXX | CLEAN: 0
<- 09:43:44 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:44 SUBSCRIBE | PACKET_ID: 2 | TOPIC_NAME: $iothub/twin/res/# | QOS: 0 | TOPIC_NAME: $iothub/methods/POST/# | QOS: 0
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/ | PACKET_ID: 3 | PAYLOAD_LEN: 19
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat1 | PACKET_ID: 4 | PAYLOAD_LEN: 21
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat2 | PACKET_ID: 5 | PAYLOAD_LEN: 21

Als operator in uw Azure IoT Central-toepassing kunt u:

  • Bekijk de telemetrie die is verzonden door de twee thermostaatonderdelen op de pagina Overzicht :

    Screenshot that shows the device overview page.

  • Bekijk de apparaateigenschappen op de pagina Info . Op deze pagina worden de eigenschappen van het apparaatinformatieonderdeel en de twee thermostaatonderdelen weergegeven:

    Screenshot that shows the device properties view.

Het apparaatsjabloon aanpassen

Als oplossingsontwikkelaar kunt u de apparaatsjabloon aanpassen die door IoT Central automatisch wordt gemaakt wanneer het apparaat voor de temperatuurcontroller is verbonden.

Als u een cloudeigenschap wilt toevoegen om de naam van de klant op te slaan die aan het apparaat is gekoppeld:

  1. Navigeer in uw IoT Central-toepassing naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Selecteer in het model Temperatuurregelaar de optie +Mogelijkheid toevoegen.

  3. Voer de naam van de klant in als weergavenaam, selecteer cloudeigenschap als het mogelijkheidstype, vouw de vermelding uit en kies Tekenreeks als het schema. Selecteer vervolgens Opslaan.

Ga als volgt te werk om aan te passen hoe de rapportopdrachten Max-Min ophalen worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Vervang voor getMaxMinReport (thermostat1) het rapport Max-Min ophalen doorhet statusrapport thermostaat1 ophalen.

  3. Vervang voor getMaxMinReport (thermostat2) het rapport Max-Min ophalen doorhet statusrapport Thermostaat2 ophalen.

  4. Selecteer Opslaan.

U kunt als volgt aanpassen hoe de beschrijfbare eigenschappen van de doeltemperatuur worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Voor targetTemperature (thermostaat1) vervangt u de doeltemperatuur door doeltemperatuur (1).

  3. Voor targetTemperature (thermostaat2) vervangt u doeltemperatuur door doeltemperatuur (2).

  4. Selecteer Opslaan.

De thermostaatonderdelen in het model temperatuurcontroller bevatten de beschrijfbare eigenschap Doeltemperatuur , de apparaatsjabloon bevat de cloudeigenschap Klantnaam . Een weergave maken die een operator kan gebruiken om deze eigenschappen te bewerken:

  1. Selecteer Weergaven en vervolgens de tegel Apparaat- en cloudgegevens bewerken.

  2. Voer Eigenschappen in als de formuliernaam.

  3. Selecteer de eigenschappen Doeltemperatuur (1), Doeltemperatuur (2) en Klantnaam . Selecteer vervolgens Sectie toevoegen.

  4. Sla uw wijzigingen op.

Screenshot that shows a view for updating property values.

De apparaatsjabloon publiceren

Voordat een operator de aanpassingen kan zien en gebruiken die u hebt gemaakt, moet u de apparaatsjabloon publiceren.

Selecteer In de apparaatsjabloonThermostaat de optie Publiceren. Selecteer Publiceren in het deelvenster Dit apparaatsjabloon op de toepassing publiceren.

Een operator kan nu de weergave Eigenschappen gebruiken om de eigenschapswaarden bij te werken en opdrachten aan te roepen met de naam Statusrapport Thermostaat1 ophalen en Thermostaat2-statusrapport ophalen op de pagina met apparaatopdrachten:

  • Schrijfbare eigenschapswaarden bijwerken op de pagina Eigenschappen :

    Screenshot that shows updating the device properties.

  • Roep de opdrachten aan vanaf de pagina Opdrachten . Als u de opdracht statusrapport uitvoert, selecteert u een datum en tijd voor de parameter Sinds voordat u deze uitvoert:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

U kunt zien hoe het apparaat reageert op opdrachten en updates van eigenschappen:

<- 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/POST/thermostat1*getMaxMinReport/?$rid=1 | PAYLOAD_LEN: 26
Info: Received PnP command for component=thermostat1, command=getMaxMinReport
Info: Returning success from command request for component=thermostat1
-> 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/res/200/?$rid=1 | PAYLOAD_LEN: 117

...

<- 09:50:04 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/twin/PATCH/properties/desired/?$version=2 | PAYLOAD_LEN: 63
Info: Received targetTemperature=67.000000 for component=thermostat2
Info: Sending acknowledgement of property to IoTHub for component=thermostat2

Browse code

Vereisten

Als u de stappen in dit artikel wilt uitvoeren, hebt u de volgende resources nodig:

De code bekijken

Open in de kopie van de Microsoft Azure IoT SDK voor C#-opslagplaats die u eerder hebt gedownload het oplossingsbestand azure-iot-sdk-csharp-main\azureiot.sln in Visual Studio. Vouw in Solution Explorer de map PnpDeviceSamples > TemperatureController uit en open de bestanden Program.cs en TemperatureControllerSample.cs om de code voor dit voorbeeld weer te geven.

In het voorbeeld wordt het digital twin definition Language-model met meerdere onderdelen geïmplementeerd.

Wanneer u het voorbeeld uitvoert om verbinding te maken met IoT Central, wordt daarvoor gebruikgemaakt van de Device Provisioning Service (DPS) om het apparaat te registreren en een verbindingsreeks te genereren. Het voorbeeld haalt de DPS-verbindingsgegevens die nodig zijn op vanuit de omgeving.

In Program.cs roept de methode MainSetupDeviceClientAsync aan:

  • Gebruik de model-id dtmi:com:example:TemperatureController;2 wanneer het apparaat wordt ingericht met DPS. IoT Central gebruikt de model-id om de apparaatsjabloon voor dit apparaat te identificeren of te genereren. Zie Een apparaat toewijzen aan een apparaatsjabloon voor meer informatie.
  • Maak een exemplaar van DeviceClient om verbinding te maken met IoT Central.
private static async Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken)
{
  DeviceClient deviceClient;
  switch (parameters.DeviceSecurityType.ToLowerInvariant())
  {
    case "dps":
      DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken);
      var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey);
      deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
      break;

    case "connectionstring":
      // ...

    default:
      // ...
  }
  return deviceClient;
}

De hoofdmethode maakt vervolgens een TemperatureControllerSample-exemplaar en roept de PerformOperationsAsync methode aan om de interacties met IoT Central af te handelen.

In TemperatureControllerSample.cs de PerformOperationsAsync methode:

  • Hiermee stelt u een handler in voor de opdracht voor opnieuw opstarten op het standaardonderdeel.
  • Hiermee stelt u handlers in voor de opdrachten getMaxMinReport op de twee thermostaatonderdelen.
  • Hiermee stelt u handlers in voor het ontvangen van updates van de doeltemperatuureigenschappen op de twee thermostaatonderdelen.
  • Hiermee worden de eerste updates van de eigenschap apparaatgegevens verzonden.
  • Verzendt periodiek temperatuurtelemetrie van de twee thermostaatonderdelen.
  • Verzendt periodiek werksettelemetrie van het standaardonderdeel.
  • Hiermee wordt de maximumtemperatuur verzonden sinds de laatste herstart wanneer een nieuwe maximumtemperatuur wordt bereikt in de twee thermostaatonderdelen.
public async Task PerformOperationsAsync(CancellationToken cancellationToken)
{
  await _deviceClient.SetMethodHandlerAsync("reboot", HandleRebootCommandAsync, _deviceClient, cancellationToken);

  // For a component-level command, the command name is in the format "<component-name>*<command-name>".
  await _deviceClient.SetMethodHandlerAsync("thermostat1*getMaxMinReport", HandleMaxMinReportCommand, Thermostat1, cancellationToken);
  await _deviceClient.SetMethodHandlerAsync("thermostat2*getMaxMinReport", HandleMaxMinReportCommand, Thermostat2, cancellationToken);

  await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(SetDesiredPropertyUpdateCallback, null, cancellationToken);
  _desiredPropertyUpdateCallbacks.Add(Thermostat1, TargetTemperatureUpdateCallbackAsync);
  _desiredPropertyUpdateCallbacks.Add(Thermostat2, TargetTemperatureUpdateCallbackAsync);

  await UpdateDeviceInformationAsync(cancellationToken);
  await SendDeviceSerialNumberAsync(cancellationToken);

  bool temperatureReset = true;
  _maxTemp[Thermostat1] = 0d;
  _maxTemp[Thermostat2] = 0d;

  while (!cancellationToken.IsCancellationRequested)
  {
    if (temperatureReset)
    {
      // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
      _temperature[Thermostat1] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
      _temperature[Thermostat2] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
    }

    await SendTemperatureAsync(Thermostat1, cancellationToken);
    await SendTemperatureAsync(Thermostat2, cancellationToken);
    await SendDeviceMemoryAsync(cancellationToken);

    temperatureReset = _temperature[Thermostat1] == 0 && _temperature[Thermostat2] == 0;
    await Task.Delay(5 * 1000);
  }
}

De SendTemperatureAsync methode laat zien hoe het apparaat de temperatuurtelemetrie verzendt van een onderdeel naar IoT Central. De SendTemperatureTelemetryAsync methode gebruikt de PnpConvention klasse om het bericht samen te stellen:

private async Task SendTemperatureAsync(string componentName, CancellationToken cancellationToken)
{
  await SendTemperatureTelemetryAsync(componentName, cancellationToken);

  double maxTemp = _temperatureReadingsDateTimeOffset[componentName].Values.Max<double>();
  if (maxTemp > _maxTemp[componentName])
  {
    _maxTemp[componentName] = maxTemp;
    await UpdateMaxTemperatureSinceLastRebootAsync(componentName, cancellationToken);
  }
}

private async Task SendTemperatureTelemetryAsync(string componentName, CancellationToken cancellationToken)
{
  const string telemetryName = "temperature";
  double currentTemperature = _temperature[componentName];
  using Message msg = PnpConvention.CreateMessage(telemetryName, currentTemperature, componentName);

  await _deviceClient.SendEventAsync(msg, cancellationToken);

  if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
  {
    _temperatureReadingsDateTimeOffset[componentName].TryAdd(DateTimeOffset.UtcNow, currentTemperature);
  }
  else
  {
    _temperatureReadingsDateTimeOffset.TryAdd(
      componentName,
      new Dictionary<DateTimeOffset, double>
      {
        { DateTimeOffset.UtcNow, currentTemperature },
      });
  }
}

De UpdateMaxTemperatureSinceLastRebootAsync methode verzendt een eigenschapsupdate maxTempSinceLastReboot naar IoT Central. Deze methode gebruikt de PnpConvention klasse om de patch te maken:

private async Task UpdateMaxTemperatureSinceLastRebootAsync(string componentName, CancellationToken cancellationToken)
{
  const string propertyName = "maxTempSinceLastReboot";
  double maxTemp = _maxTemp[componentName];
  TwinCollection reportedProperties = PnpConvention.CreateComponentPropertyPatch(componentName, propertyName, maxTemp);

  await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
}

De TargetTemperatureUpdateCallbackAsync methode verwerkt de beschrijfbare update van de doeltemperatuur-eigenschap van IoT Central. Deze methode gebruikt de PnpConvention klasse om het bericht voor het bijwerken van de eigenschap te lezen en het antwoord samen te stellen:

private async Task TargetTemperatureUpdateCallbackAsync(TwinCollection desiredProperties, object userContext)
{
  const string propertyName = "targetTemperature";
  string componentName = (string)userContext;

  bool targetTempUpdateReceived = PnpConvention.TryGetPropertyFromTwin(
    desiredProperties,
    propertyName,
    out double targetTemperature,
    componentName);
  if (!targetTempUpdateReceived)
  {
      return;
  }

  TwinCollection pendingReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      targetTemperature,
      (int)StatusCode.InProgress,
      desiredProperties.Version);

  await _deviceClient.UpdateReportedPropertiesAsync(pendingReportedProperty);

  // Update Temperature in 2 steps
  double step = (targetTemperature - _temperature[componentName]) / 2d;
  for (int i = 1; i <= 2; i++)
  {
      _temperature[componentName] = Math.Round(_temperature[componentName] + step, 1);
      await Task.Delay(6 * 1000);
  }

  TwinCollection completedReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      _temperature[componentName],
      (int)StatusCode.Completed,
      desiredProperties.Version,
      "Successfully updated target temperature");

  await _deviceClient.UpdateReportedPropertiesAsync(completedReportedProperty);
}

De HandleMaxMinReportCommand methode verwerkt de opdrachten voor de onderdelen die worden aangeroepen vanuit IoT Central:

private Task<MethodResponse> HandleMaxMinReportCommand(MethodRequest request, object userContext)
{
    try
    {
        string componentName = (string)userContext;
        DateTime sinceInUtc = JsonConvert.DeserializeObject<DateTime>(request.DataAsJson);
        var sinceInDateTimeOffset = new DateTimeOffset(sinceInUtc);

        if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
        {

            Dictionary<DateTimeOffset, double> allReadings = _temperatureReadingsDateTimeOffset[componentName];
            Dictionary<DateTimeOffset, double> filteredReadings = allReadings.Where(i => i.Key > sinceInDateTimeOffset)
                .ToDictionary(i => i.Key, i => i.Value);

            if (filteredReadings != null && filteredReadings.Any())
            {
                var report = new
                {
                    maxTemp = filteredReadings.Values.Max<double>(),
                    minTemp = filteredReadings.Values.Min<double>(),
                    avgTemp = filteredReadings.Values.Average(),
                    startTime = filteredReadings.Keys.Min(),
                    endTime = filteredReadings.Keys.Max(),
                };

                byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
                return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
            }

            return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
        }

        return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
    }
    catch (JsonReaderException ex)
    {
        // ...
    }
}

Verbindingsgegevens ophalen

Wanneer u de toepassing voor het voorbeeldapparaat later in deze zelfstudie uitvoert, hebt u de volgende configuratiewaarden nodig:

  • Id-bereik: Navigeer in uw IoT Central-toepassing naar verbindingsgroepen voor machtigingen>. Noteer de waarde van Id-bereik.
  • Primaire sleutel groeperen: Navigeer in uw IoT Central-toepassing naar Verbindingsgroepen > voor machtigingen > voor SAS-IoT-Devices. Noteer de waarde van Primaire sleutel onder Shared Access Signature (SAS).

Gebruik Azure Cloud Shell om een apparaatsleutel te genereren op basis van de primaire sleutel van de groep die u hebt opgehaald:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Noteer de gegenereerde apparaatsleutel. U hebt deze waarde verderop in deze zelfstudie nodig.

Notitie

Als u dit voorbeeld wilt uitvoeren, hoeft u het apparaat niet vooraf te registreren in uw IoT Central-toepassing. In het voorbeeld wordt de IoT Central-mogelijkheid gebruikt om apparaten automatisch te registreren wanneer ze voor het eerst verbinding maken.

De code uitvoeren

Notitie

Stel TemperatureController in als opstartproject voordat u de code uitvoert.

De voorbeeldtoepassing uitvoeren in Visual Studio:

  1. Selecteer in Solution Explorer het projectbestand PnpDeviceSamples > TemperatureController.

  2. Navigeer naar Debug-eigenschappen >van Project > TemperatureController. Voeg vervolgens de volgende omgevingsvariabelen toe aan het project:

    Naam Weergegeven als
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE De waarde van het id-bereik dat u eerder hebt genoteerd.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY De gegenereerde waarde van de apparaatsleutel die u eerder hebt genoteerd.

U kunt het voorbeeld nu uitvoeren en fouten opsporen in Visual Studio.

In de volgende uitvoer ziet u hoe het apparaat zich registreert bij IoT Central en er verbinding mee maakt. Het voorbeeld begint met het verzenden van telemetriegegevens:

[03/31/2021 14:43:17]info: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Press Control+C to quit the sample.
[03/31/2021 14:43:17]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set up the device client.
[03/31/2021 14:43:18]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Initializing via DPS
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for 'reboot' command.
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Connection status change registered - status=Connected, reason=Connection_Ok.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for "getMaxMinReport" command.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler to receive 'targetTemperature' updates.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component = 'deviceInformation', properties update is complete.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - { "serialNumber": "SR-123456" } is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 34.2 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", { "maxTempSinceLastReboot": 34.2 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat2", { "temperature": 25.1 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat2", { "maxTempSinceLastReboot": 25.1 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - {"workingSet":31412} in KB.

Als operator in uw Azure IoT Central-toepassing kunt u:

  • Bekijk de telemetrie die is verzonden door de twee thermostaatonderdelen op de pagina Overzicht :

    Screenshot that shows the device overview page.

  • Bekijk de apparaateigenschappen op de pagina Info . Op deze pagina worden de eigenschappen van het apparaatinformatieonderdeel en de twee thermostaatonderdelen weergegeven:

    Screenshot that shows the device properties view.

Het apparaatsjabloon aanpassen

Als oplossingsontwikkelaar kunt u de apparaatsjabloon aanpassen die door IoT Central automatisch wordt gemaakt wanneer het apparaat voor de temperatuurcontroller is verbonden.

Als u een cloudeigenschap wilt toevoegen om de naam van de klant op te slaan die aan het apparaat is gekoppeld:

  1. Navigeer in uw IoT Central-toepassing naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Selecteer in het model Temperatuurregelaar de optie +Mogelijkheid toevoegen.

  3. Voer de naam van de klant in als weergavenaam, selecteer cloudeigenschap als het mogelijkheidstype, vouw de vermelding uit en kies Tekenreeks als het schema. Selecteer vervolgens Opslaan.

Ga als volgt te werk om aan te passen hoe de rapportopdrachten Max-Min ophalen worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Vervang voor getMaxMinReport (thermostat1) het rapport Max-Min ophalen doorhet statusrapport thermostaat1 ophalen.

  3. Vervang voor getMaxMinReport (thermostat2) het rapport Max-Min ophalen doorhet statusrapport Thermostaat2 ophalen.

  4. Selecteer Opslaan.

U kunt als volgt aanpassen hoe de beschrijfbare eigenschappen van de doeltemperatuur worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Voor targetTemperature (thermostaat1) vervangt u de doeltemperatuur door doeltemperatuur (1).

  3. Voor targetTemperature (thermostaat2) vervangt u doeltemperatuur door doeltemperatuur (2).

  4. Selecteer Opslaan.

De thermostaatonderdelen in het model temperatuurcontroller bevatten de beschrijfbare eigenschap Doeltemperatuur , de apparaatsjabloon bevat de cloudeigenschap Klantnaam . Een weergave maken die een operator kan gebruiken om deze eigenschappen te bewerken:

  1. Selecteer Weergaven en vervolgens de tegel Apparaat- en cloudgegevens bewerken.

  2. Voer Eigenschappen in als de formuliernaam.

  3. Selecteer de eigenschappen Doeltemperatuur (1), Doeltemperatuur (2) en Klantnaam . Selecteer vervolgens Sectie toevoegen.

  4. Sla uw wijzigingen op.

Screenshot that shows a view for updating property values.

De apparaatsjabloon publiceren

Voordat een operator de aanpassingen kan zien en gebruiken die u hebt gemaakt, moet u de apparaatsjabloon publiceren.

Selecteer In de apparaatsjabloonThermostaat de optie Publiceren. Selecteer Publiceren in het deelvenster Dit apparaatsjabloon op de toepassing publiceren.

Een operator kan nu de weergave Eigenschappen gebruiken om de eigenschapswaarden bij te werken en opdrachten aan te roepen met de naam Statusrapport Thermostaat1 ophalen en Thermostaat2-statusrapport ophalen op de pagina met apparaatopdrachten:

  • Schrijfbare eigenschapswaarden bijwerken op de pagina Eigenschappen :

    Screenshot that shows updating the device properties.

  • Roep de opdrachten aan vanaf de pagina Opdrachten . Als u de opdracht statusrapport uitvoert, selecteert u een datum en tijd voor de parameter Sinds voordat u deze uitvoert:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

U kunt zien hoe het apparaat reageert op opdrachten en updates van eigenschappen:

[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: Received - component="thermostat2", generating max, min and avg temperature report since 31/03/2021 06:00:00.
[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: component="thermostat2", MaxMinReport since 31/03/2021 06:00:00: maxTemp=36.4, minTemp=36.4, avgTemp=36.4, startTime=31/03/2021 14:46:33, endTime=31/03/2021 14:46:55

...

[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Received - component="thermostat1", { "targetTemperature": 67°C }.
[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is InProgress.
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is Completed
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 67 } in °C.

Browse code

Vereisten

Als u de stappen in dit artikel wilt uitvoeren, hebt u de volgende resources nodig:

  • Een ontwikkelcomputer met Java SE Development Kit 8 of hoger. Zie De JDK installeren voor meer informatie.

  • Apache Maven 3.

  • Een lokale kopie van de Microsoft Azure IoT-SDK voor Java GitHub-opslagplaats die de voorbeeldcode bevat. Gebruik deze koppeling om een kopie van de opslagplaats te downloaden: ZIP downloaden. Pak het bestand vervolgens uit op een geschikte locatie op uw lokale computer.

De code bekijken

Open in de kopie van de Microsoft Azure IoT SDK voor Java die u eerder hebt gedownload de azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java-bestand in een teksteditor.

In het voorbeeld wordt het digital twin definition Language-model met meerdere onderdelen geïmplementeerd.

Wanneer u het voorbeeld uitvoert om verbinding te maken met IoT Central, wordt daarvoor gebruikgemaakt van de Device Provisioning Service (DPS) om het apparaat te registreren en een verbindingsreeks te genereren. Het voorbeeld haalt de DPS-verbindingsgegevens die nodig zijn op uit de opdrachtregelomgeving.

De methode main:

  • Roept initializeAndProvisionDevice aan om de model-id dtmi:com:example:TemperatureController;2 in te stellen, gebruikt DPS om het apparaat in te richten en registreren, maakt een DeviceClient-instantie en maakt verbinding met uw IoT Central-toepassing. IoT Central gebruikt de model-id om de apparaatsjabloon voor dit apparaat te identificeren of te genereren. Zie Een apparaat toewijzen aan een apparaatsjabloon voor meer informatie.
  • Hiermee maakt u opdrachthandlers voor de getMaxMinReport en reboot opdrachten.
  • Hiermee maakt u update-handlers voor eigenschappen voor de beschrijfbare targetTemperature eigenschappen.
  • Verzendt initiële waarden voor de eigenschappen in de interface Apparaatgegevens en de eigenschappen Apparaatgeheugen en Serienummer .
  • Hiermee wordt een thread gestart om temperatuurtelemetrie van de twee thermostaten te verzenden en de maxTempSinceLastReboot eigenschap elke vijf seconden bij te werken.
public static void main(String[] args) throws Exception {

  // ...
  
  switch (deviceSecurityType.toLowerCase())
  {
    case "dps":
    {
      if (validateArgsForDpsFlow())
      {
        initializeAndProvisionDevice();
        break;
      }
      throw new IllegalArgumentException("Required environment variables are not set for DPS flow, please recheck your environment.");
    }
    case "connectionstring":
    {
      // ...
    }
    default:
    {
      // ...
    }
  }
  
  deviceClient.subscribeToMethods(new MethodCallback(), null);
  
  deviceClient.subscribeToDesiredPropertiesAsync(
  {
  (twin, context) ->
      TwinCollection desiredProperties = twin.getDesiredProperties();
      for (String desiredPropertyKey : desiredProperties.keySet())
      {
          TargetTemperatureUpdateCallback.onPropertyChanged(new Property(desiredPropertyKey, desiredProperties.get(desiredPropertyKey)), null);
      }
  },
  null,
  (exception, context) ->
  {
      if (exception == null)
      {
          log.info("Successfully subscribed to desired properties. Getting initial state");
          deviceClient.getTwinAsync(
              (twin, getTwinException, getTwinContext) ->
              {
                  log.info("Initial twin state received");
                  log.info(twin.toString());
              },
              null);
      }
      else
      {
          log.info("Failed to subscribe to desired properties. Error code {}", exception.getStatusCode());
          System.exit(-1);
      }
  },
  null);

  updateDeviceInformation();
  sendDeviceMemory();
  sendDeviceSerialNumber();
  
  final AtomicBoolean temperatureReset = new AtomicBoolean(true);
  maxTemperature.put(THERMOSTAT_1, 0.0d);
  maxTemperature.put(THERMOSTAT_2, 0.0d);
  
  new Thread(new Runnable() {
    @SneakyThrows({InterruptedException.class, IOException.class})
    @Override
    public void run() {
      while (true) {
        if (temperatureReset.get()) {
          // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
          temperature.put(THERMOSTAT_1, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
          temperature.put(THERMOSTAT_2, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
        }

        sendTemperatureReading(THERMOSTAT_1);
        sendTemperatureReading(THERMOSTAT_2);

        temperatureReset.set(temperature.get(THERMOSTAT_1) == 0 && temperature.get(THERMOSTAT_2) == 0);
        Thread.sleep(5 * 1000);
      }
    }
  }).start();
}

De methode initializeAndProvisionDevice laat zien hoe het apparaat DPS gebruikt om te registreren en verbinding te maken met IoT Central. De nettolading bevat de model-id die Door IoT Central wordt gebruikt om een apparaat toe te wijzen aan een apparaatsjabloon:

private static void initializeAndProvisionDevice() throws Exception {
  SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(deviceSymmetricKey.getBytes(), registrationId);
  ProvisioningDeviceClient provisioningDeviceClient;
  ProvisioningStatus provisioningStatus = new ProvisioningStatus();

  provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityClientSymmetricKey);

  AdditionalData additionalData = new AdditionalData();
  additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));

  ProvisioningDeviceClientRegistrationResult registrationResult = provisioningDeviceClient.registerDeviceSync(additionalData);

    ClientOptions options = ClientOptions.builder().modelId(MODEL_ID).build();
    if (registrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) {
        System.out.println("IotHUb Uri : " + registrationResult.getIothubUri());
        System.out.println("Device ID : " + registrationResult.getDeviceId());
        String iotHubUri = registrationResult.getIothubUri();
        String deviceId = registrationResult.getDeviceId();
        log.debug("Opening the device client.");
        deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT, options);
        deviceClient.open(true);
    }
}

De sendTemperatureTelemetry methode laat zien hoe het apparaat de temperatuurtelemetrie verzendt van een onderdeel naar IoT Central. Deze methode gebruikt de PnpConvention klasse om het bericht te maken:

  private static void sendTemperatureTelemetry(String componentName) {
    String telemetryName = "temperature";
    double currentTemperature = temperature.get(componentName);

    Message message = PnpConvention.createIotHubMessageUtf8(telemetryName, currentTemperature, componentName);
    deviceClient.sendEventAsync(message, new MessageIotHubEventCallback(), message);

    // Add the current temperature entry to the list of temperature readings.
    Map<Date, Double> currentReadings;
    if (temperatureReadings.containsKey(componentName)) {
      currentReadings = temperatureReadings.get(componentName);
    } else {
      currentReadings = new HashMap<>();
    }
    currentReadings.put(new Date(), currentTemperature);
    temperatureReadings.put(componentName, currentReadings);
  }

De updateMaxTemperatureSinceLastReboot methode verzendt een eigenschapsupdate maxTempSinceLastReboot van een onderdeel naar IoT Central. Deze methode gebruikt de PnpConvention klasse om de patch te maken:

private static void updateMaxTemperatureSinceLastReboot(String componentName) throws IOException {
  String propertyName = "maxTempSinceLastReboot";
  double maxTemp = maxTemperature.get(componentName);

  TwinCollection reportedProperty = PnpConvention.createComponentPropertyPatch(propertyName, maxTemp, componentName);
  deviceClient.updateReportedPropertiesAsync(reportedProperty, sendReportedPropertiesResponseCallback, null);
  log.debug("Property: Update - {\"{}\": {}°C} is {}.", propertyName, maxTemp, StatusCode.COMPLETED);
}

De TargetTemperatureUpdateCallback klasse bevat de methode voor het onPropertyChanged afhandelen van beschrijfbare eigenschapsupdates voor een onderdeel van IoT Central. Deze methode maakt gebruik van de PnpConvention klasse om het antwoord te maken:

private static class TargetTemperatureUpdateCallback
{
    final static String propertyName = "targetTemperature";
    @SneakyThrows(InterruptedException.class)
    public static void onPropertyChanged(Property property, Object context) {
        String componentName = (String) context;
        if (property.getKey().equalsIgnoreCase(componentName)) {
            double targetTemperature = (double) ((TwinCollection) property.getValue()).get(propertyName);
            log.debug("Property: Received - component=\"{}\", {\"{}\": {}°C}.", componentName, propertyName, targetTemperature);
            TwinCollection pendingPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    targetTemperature,
                    componentName,
                    StatusCode.IN_PROGRESS.value,
                    property.getVersion().longValue(),
                    null);
            deviceClient.updateReportedPropertiesAsync(pendingPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - component=\"{}\", {\"{}\": {}°C} is {}", componentName, propertyName, targetTemperature, StatusCode.IN_PROGRESS);
            // Update temperature in 2 steps
            double step = (targetTemperature - temperature.get(componentName)) / 2;
            for (int i = 1; i <=2; i++) {
                temperature.put(componentName, BigDecimal.valueOf(temperature.get(componentName) + step).setScale(1, RoundingMode.HALF_UP).doubleValue());
                Thread.sleep(5 * 1000);
            }
            TwinCollection completedPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    temperature.get(componentName),
                    componentName,
                    StatusCode.COMPLETED.value,
                    property.getVersion().longValue(),
                    "Successfully updated target temperature.");
            deviceClient.updateReportedPropertiesAsync(completedPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - {\"{}\": {}°C} is {}", propertyName, temperature.get(componentName), StatusCode.COMPLETED);
        } else {
            log.debug("Property: Received an unrecognized property update from service.");
        }
    }
}

De MethodCallback klasse bevat de methode voor het onMethodInvoked afhandelen van onderdeelopdrachten die worden aangeroepen vanuit IoT Central:

private static class MethodCallback implements com.microsoft.azure.sdk.iot.device.twin.MethodCallback
{
    final String reboot = "reboot";
    final String getMaxMinReport1 = "thermostat1*getMaxMinReport";
    final String getMaxMinReport2 = "thermostat2*getMaxMinReport";
    @SneakyThrows(InterruptedException.class)
    @Override
    public DirectMethodResponse onMethodInvoked(String methodName, DirectMethodPayload methodData, Object context) {
        String jsonRequest = methodData.getPayload(String.class);
        switch (methodName) {
            case reboot:
                int delay = getCommandRequestValue(jsonRequest, Integer.class);
                log.debug("Command: Received - Rebooting thermostat (resetting temperature reading to 0°C after {} seconds).", delay);
                Thread.sleep(delay * 1000L);
                temperature.put(THERMOSTAT_1, 0.0d);
                temperature.put(THERMOSTAT_2, 0.0d);
                maxTemperature.put(THERMOSTAT_1, 0.0d);
                maxTemperature.put(THERMOSTAT_2, 0.0d);
                temperatureReadings.clear();
                return new DirectMethodResponse(StatusCode.COMPLETED.value, null);
            case getMaxMinReport1:
            case getMaxMinReport2:
                String[] words = methodName.split("\\*");
                String componentName = words[0];
                if (temperatureReadings.containsKey(componentName)) {
                    Date since = getCommandRequestValue(jsonRequest, Date.class);
                    log.debug("Command: Received - component=\"{}\", generating min, max, avg temperature report since {}", componentName, since);
                    Map<Date, Double> allReadings = temperatureReadings.get(componentName);
                    Map<Date, Double> filteredReadings = allReadings.entrySet().stream()
                            .filter(map -> map.getKey().after(since))
                            .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
                    if (!filteredReadings.isEmpty()) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                        double maxTemp = Collections.max(filteredReadings.values());
                        double minTemp = Collections.min(filteredReadings.values());
                        double avgTemp = filteredReadings.values().stream().mapToDouble(Double::doubleValue).average().orElse(Double.NaN);
                        String startTime =  sdf.format(Collections.min(filteredReadings.keySet()));
                        String endTime =  sdf.format(Collections.max(filteredReadings.keySet()));
                        String responsePayload = String.format(
                                "{\"maxTemp\": %.1f, \"minTemp\": %.1f, \"avgTemp\": %.1f, \"startTime\": \"%s\", \"endTime\": \"%s\"}",
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        log.debug("Command: MaxMinReport since {}: \"maxTemp\": {}°C, \"minTemp\": {}°C, \"avgTemp\": {}°C, \"startTime\": {}, \"endTime\": {}",
                                since,
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        return new DirectMethodResponse(StatusCode.COMPLETED.value, responsePayload);
                    }
                    log.debug("Command: component=\"{}\", no relevant readings found since {}, cannot generate any report.", componentName, since);
                    return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
                }
                log.debug("Command: component=\"{}\", no temperature readings sent yet, cannot generate any report.", componentName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
            default:
                log.debug("Command: command=\"{}\" is not implemented, no action taken.", methodName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
        }
    }
}

Verbindingsgegevens ophalen

Wanneer u de toepassing voor het voorbeeldapparaat later in deze zelfstudie uitvoert, hebt u de volgende configuratiewaarden nodig:

  • Id-bereik: Navigeer in uw IoT Central-toepassing naar verbindingsgroepen voor machtigingen>. Noteer de waarde van Id-bereik.
  • Primaire sleutel groeperen: Navigeer in uw IoT Central-toepassing naar Verbindingsgroepen > voor machtigingen > voor SAS-IoT-Devices. Noteer de waarde van Primaire sleutel onder Shared Access Signature (SAS).

Gebruik Azure Cloud Shell om een apparaatsleutel te genereren op basis van de primaire sleutel van de groep die u hebt opgehaald:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Noteer de gegenereerde apparaatsleutel. U hebt deze waarde verderop in deze zelfstudie nodig.

Notitie

Als u dit voorbeeld wilt uitvoeren, hoeft u het apparaat niet vooraf te registreren in uw IoT Central-toepassing. In het voorbeeld wordt de IoT Central-mogelijkheid gebruikt om apparaten automatisch te registreren wanneer ze voor het eerst verbinding maken.

Navigeer in Windows naar de hoofdmap van de Azure IoT SDK voor Java-opslagplaats die u hebt gedownload.

Voer de volgende opdracht uit om de voorbeeldtoepassing te maken:

mvn install -T 2C -DskipTests

De code uitvoeren

Als u de voorbeeldtoepassing wilt uitvoeren, opent u een opdrachtregelomgeving en navigeert u naar de map azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample folder die de src-map bevat met het TemperatureController.java voorbeeldbestand.

Stel de omgevingsvariabelen in om het voorbeeld te configureren. Het volgende codefragment laat zien hoe u de omgevingsvariabelen instelt vanaf de Windows-opdrachtprompt. Als u een bash-shell gebruikt, vervangt u de set-opdrachten door export-opdrachten:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Voer het voorbeeld uit:

mvn exec:java -Dexec.mainClass="samples.com.microsoft.azure.sdk.iot.device.TemperatureController"

In de volgende uitvoer ziet u hoe het apparaat zich registreert bij IoT Central en er verbinding mee maakt. Het voorbeeld begint met het verzenden van telemetriegegevens:

2021-03-30 15:33:25.138 DEBUG TemperatureController:123 - Initialize the device client.
Waiting for Provisioning Service to register
Waiting for Provisioning Service to register
IotHUb Uri : iotc-60a.....azure-devices.net
Device ID : sample-device-01
2021-03-30 15:33:38.294 DEBUG TemperatureController:247 - Opening the device client.
2021-03-30 15:33:38.307 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.321 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.427 DEBUG MqttIotHubConnection:274 - Opening MQTT connection...
2021-03-30 15:33:38.427 DEBUG Mqtt:123 - Sending MQTT CONNECT packet...
2021-03-30 15:33:44.628 DEBUG Mqtt:126 - Sent MQTT CONNECT packet was acknowledged
2021-03-30 15:33:44.630 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/#
2021-03-30 15:33:44.731 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/# was acknowledged
2021-03-30 15:33:44.733 DEBUG MqttIotHubConnection:279 - MQTT connection opened successfully
2021-03-30 15:33:44.733 DEBUG IotHubTransport:302 - The connection to the IoT Hub has been established
2021-03-30 15:33:44.734 INFO  IotHubTransport:1429 - Updating transport status to new status CONNECTED with reason CONNECTION_OK
2021-03-30 15:33:44.735 DEBUG IotHubTransport:1439 - Invoking connection status callbacks with new status details
2021-03-30 15:33:44.739 DEBUG IotHubTransport:394 - Client connection opened successfully
2021-03-30 15:33:44.740 INFO  DeviceClient:438 - Device client opened successfully
2021-03-30 15:33:44.740 DEBUG TemperatureController:152 - Set handler for "reboot" command.
2021-03-30 15:33:44.742 DEBUG TemperatureController:153 - Set handler for "getMaxMinReport" command.
2021-03-30 15:33:44.774 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.774 DEBUG TemperatureController:156 - Set handler to receive "targetTemperature" updates.
2021-03-30 15:33:44.775 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.779 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/methods/POST/#
2021-03-30 15:33:44.793 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.794 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [417d659a-7324-43fa-84eb-8a3f3d07963c] Message Id [55532cad-8a5a-489f-9aa8-8f0e5bc21541] Request Id [0] Device Operation Type [DEVICE_OPERATION_TWIN_GET_REQUEST] )
2021-03-30 15:33:44.819 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [d46a0d8a-8a18-4014-abeb-768bd9b17ad2] Message Id [780abc81-ce42-4e5f-aa80-e4785883604e] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.881 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic $iothub/methods/POST/# was acknowledged
2021-03-30 15:33:44.882 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.882 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/twin/res/#
2021-03-30 15:33:44.893 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [a77b1c02-f043-4477-b610-e31a774772c0] Message Id [2e2f6bee-c480-42cf-ac31-194118930846] Request Id [1] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.904 DEBUG TemperatureController:423 - Property: Update - component = "deviceInformation" is COMPLETED.
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [bbb7e3cf-3550-4fdf-90f9-0787740f028a] Message Id [e06ac385-ae0d-46dd-857a-d9725707527a] )
2021-03-30 15:33:44.915 DEBUG TemperatureController:434 - Telemetry: Sent - {"workingSet": 1024.0KiB }
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [6dbef765-cc9a-4e72-980a-2fe5b0cd77e1] Message Id [49bbad33-09bf-417a-9d6e-299ba7b7c562] Request Id [2] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.916 DEBUG TemperatureController:442 - Property: Update - {"serialNumber": SR-123456} is COMPLETED
2021-03-30 15:33:44.927 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [86787c32-87a5-4c49-9083-c7f2b17446a7] Message Id [0a45fa0c-a467-499d-b214-9bb5995772ba] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id 0a45fa0c-a467-499d-b214-9bb5995772ba.

Als operator in uw Azure IoT Central-toepassing kunt u:

  • Bekijk de telemetrie die is verzonden door de twee thermostaatonderdelen op de pagina Overzicht :

    Screenshot that shows the device overview page.

  • Bekijk de apparaateigenschappen op de pagina Info . Op deze pagina worden de eigenschappen van het apparaatinformatieonderdeel en de twee thermostaatonderdelen weergegeven:

    Screenshot that shows the device properties view.

Het apparaatsjabloon aanpassen

Als oplossingsontwikkelaar kunt u de apparaatsjabloon aanpassen die door IoT Central automatisch wordt gemaakt wanneer het apparaat voor de temperatuurcontroller is verbonden.

Als u een cloudeigenschap wilt toevoegen om de naam van de klant op te slaan die aan het apparaat is gekoppeld:

  1. Navigeer in uw IoT Central-toepassing naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Selecteer in het model Temperatuurregelaar de optie +Mogelijkheid toevoegen.

  3. Voer de naam van de klant in als weergavenaam, selecteer cloudeigenschap als het mogelijkheidstype, vouw de vermelding uit en kies Tekenreeks als het schema. Selecteer vervolgens Opslaan.

Ga als volgt te werk om aan te passen hoe de rapportopdrachten Max-Min ophalen worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Vervang voor getMaxMinReport (thermostat1) het rapport Max-Min ophalen doorhet statusrapport thermostaat1 ophalen.

  3. Vervang voor getMaxMinReport (thermostat2) het rapport Max-Min ophalen doorhet statusrapport Thermostaat2 ophalen.

  4. Selecteer Opslaan.

U kunt als volgt aanpassen hoe de beschrijfbare eigenschappen van de doeltemperatuur worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Voor targetTemperature (thermostaat1) vervangt u de doeltemperatuur door doeltemperatuur (1).

  3. Voor targetTemperature (thermostaat2) vervangt u doeltemperatuur door doeltemperatuur (2).

  4. Selecteer Opslaan.

De thermostaatonderdelen in het model temperatuurcontroller bevatten de beschrijfbare eigenschap Doeltemperatuur , de apparaatsjabloon bevat de cloudeigenschap Klantnaam . Een weergave maken die een operator kan gebruiken om deze eigenschappen te bewerken:

  1. Selecteer Weergaven en vervolgens de tegel Apparaat- en cloudgegevens bewerken.

  2. Voer Eigenschappen in als de formuliernaam.

  3. Selecteer de eigenschappen Doeltemperatuur (1), Doeltemperatuur (2) en Klantnaam . Selecteer vervolgens Sectie toevoegen.

  4. Sla uw wijzigingen op.

Screenshot that shows a view for updating property values.

De apparaatsjabloon publiceren

Voordat een operator de aanpassingen kan zien en gebruiken die u hebt gemaakt, moet u de apparaatsjabloon publiceren.

Selecteer In de apparaatsjabloonThermostaat de optie Publiceren. Selecteer Publiceren in het deelvenster Dit apparaatsjabloon op de toepassing publiceren.

Een operator kan nu de weergave Eigenschappen gebruiken om de eigenschapswaarden bij te werken en opdrachten aan te roepen met de naam Statusrapport Thermostaat1 ophalen en Thermostaat2-statusrapport ophalen op de pagina met apparaatopdrachten:

  • Schrijfbare eigenschapswaarden bijwerken op de pagina Eigenschappen :

    Screenshot that shows updating the device properties.

  • Roep de opdrachten aan vanaf de pagina Opdrachten . Als u de opdracht statusrapport uitvoert, selecteert u een datum en tijd voor de parameter Sinds voordat u deze uitvoert:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

U kunt zien hoe het apparaat reageert op opdrachten en updates van eigenschappen:

2021-03-30 15:43:57.133 DEBUG TemperatureController:309 - Command: Received - component="thermostat1", generating min, max, avg temperature report since Tue Mar 30 06:00:00 BST 2021
2021-03-30 15:43:57.153 DEBUG TemperatureController:332 - Command: MaxMinReport since Tue Mar 30 06:00:00 BST 2021: "maxTemp": 35.6°C, "minTemp": 35.6°C, "avgTemp": 35.6°C, "startTime": 2021-03-30T15:43:41Z, "endTime": 2021-03-30T15:43:56Z
2021-03-30 15:43:57.394 DEBUG TemperatureController:502 - Command - Response from IoT Hub: command name=null, status=OK_EMPTY


...

2021-03-30 15:48:47.808 DEBUG TemperatureController:372 - Property: Received - component="thermostat2", {"targetTemperature": 67.0°C}.
2021-03-30 15:48:47.837 DEBUG TemperatureController:382 - Property: Update - component="thermostat2", {"targetTemperature": 67.0°C} is IN_PROGRESS

Browse code

Vereisten

Als u de stappen in dit artikel wilt uitvoeren, hebt u de volgende resources nodig:

  • Een ontwikkelmachine waarop Node.js versie 6 of hoger is geïnstalleerd. Voer node --version uit op de opdrachtregel om uw versie te controleren. In de instructies in deze zelfstudie wordt ervan uitgegaan dat u de opdracht node uitvoert vanaf de Windows-opdrachtprompt. U kunt Node.js echter gebruiken met verschillende andere besturingssystemen.

  • Een lokale kopie van de Microsoft Azure IoT-SDK voor Node.js GitHub-opslagplaats die de voorbeeldcode bevat. Gebruik deze koppeling om een kopie van de opslagplaats te downloaden: ZIP downloaden. Pak het bestand vervolgens uit op een geschikte locatie op uw lokale computer.

De code bekijken

Open in de kopie van de Microsoft Azure IoT SDK voor Node.js u eerder hebt gedownload het bestand azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js in een teksteditor.

In het voorbeeld wordt het digital twin definition Language-model met meerdere onderdelen geïmplementeerd.

Wanneer u het voorbeeld uitvoert om verbinding te maken met IoT Central, wordt daarvoor gebruikgemaakt van de Device Provisioning Service (DPS) om het apparaat te registreren en een verbindingsreeks te genereren. Het voorbeeld haalt de DPS-verbindingsgegevens die nodig zijn op uit de opdrachtregelomgeving.

De methode main:

  • Hiermee wordt een client-object gemaakt en wordt de id van het dtmi:com:example:TemperatureController;2-model ingesteld voordat de verbinding wordt geopend. IoT Central gebruikt de model-id om de apparaatsjabloon voor dit apparaat te identificeren of te genereren. Zie Een apparaat toewijzen aan een apparaatsjabloon voor meer informatie.
  • Hiermee maakt u opdrachthandlers voor drie opdrachten.
  • Start een lus voor elk thermostaatonderdeel om de 5 seconden temperatuurtelemetrie te verzenden.
  • Start een lus voor het standaardonderdeel voor het verzenden van telemetriegegevens van werksetgrootte om de 6 seconden.
  • Hiermee verzendt u de maxTempSinceLastReboot eigenschap voor elk thermostaatonderdeel.
  • Hiermee worden de eigenschappen van apparaatgegevens verzonden.
  • Hiermee maakt u schrijfbare eigenschappenhandlers voor de drie onderdelen.
async function main() {
  // ...

  // fromConnectionString must specify a transport, coming from any transport package.
  const client = Client.fromConnectionString(deviceConnectionString, Protocol);
  console.log('Connecting using connection string: ' + deviceConnectionString);
  let resultTwin;

  try {
    // Add the modelId here
    await client.setOptions(modelIdObject);
    await client.open();
    console.log('Enabling the commands on the client');
    client.onDeviceMethod(commandNameGetMaxMinReport1, commandHandler);
    client.onDeviceMethod(commandNameGetMaxMinReport2, commandHandler);
    client.onDeviceMethod(commandNameReboot, commandHandler);

    // Send Telemetry after some interval
    let index1 = 0;
    let index2 = 0;
    let index3 = 0;
    intervalToken1 = setInterval(() => {
      const data = JSON.stringify(thermostat1.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index1, thermostat1ComponentName).catch((err) => console.log('error ', err.toString()));
      index1 += 1;
    }, 5000);

    intervalToken2 = setInterval(() => {
      const data = JSON.stringify(thermostat2.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index2, thermostat2ComponentName).catch((err) => console.log('error ', err.toString()));
      index2 += 1;
    }, 5500);


    intervalToken3 = setInterval(() => {
      const data = JSON.stringify({ workingset: 1 + (Math.random() * 90) });
      sendTelemetry(client, data, index3, null).catch((err) => console.log('error ', err.toString()));
      index3 += 1;
    }, 6000);

    // attach a standard input exit listener
    exitListener(client);

    try {
      resultTwin = await client.getTwin();
      // Only report readable properties
      const patchRoot = helperCreateReportedPropertiesPatch({ serialNumber: serialNumber }, null);
      const patchThermostat1Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat1.getMaxTemperatureValue(),
      }, thermostat1ComponentName);

      const patchThermostat2Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat2.getMaxTemperatureValue(),
      }, thermostat2ComponentName);

      const patchDeviceInfo = helperCreateReportedPropertiesPatch({
        manufacturer: 'Contoso Device Corporation',
        model: 'Contoso 47-turbo',
        swVersion: '10.89',
        osName: 'Contoso_OS',
        processorArchitecture: 'Contoso_x86',
        processorManufacturer: 'Contoso Industries',
        totalStorage: 65000,
        totalMemory: 640,
      }, deviceInfoComponentName);

      // the below things can only happen once the twin is there
      updateComponentReportedProperties(resultTwin, patchRoot, null);
      updateComponentReportedProperties(resultTwin, patchThermostat1Info, thermostat1ComponentName);
      updateComponentReportedProperties(resultTwin, patchThermostat2Info, thermostat2ComponentName);
      updateComponentReportedProperties(resultTwin, patchDeviceInfo, deviceInfoComponentName);
      desiredPropertyPatchListener(resultTwin, [thermostat1ComponentName, thermostat2ComponentName, deviceInfoComponentName]);
    } catch (err) {
      console.error('could not retrieve twin or report twin properties\n' + err.toString());
    }
  } catch (err) {
    console.error('could not connect Plug and Play client or could not attach interval function for telemetry\n' + err.toString());
  }
}

De functie provisionDevice laat zien hoe het apparaat DPS gebruikt om te registreren en verbinding te maken met IoT Central. De nettolading bevat de model-id die IoT Central gebruikt om een apparaat toe te wijzen aan een apparaatsjabloon:

async function provisionDevice(payload) {
  var provSecurityClient = new SymmetricKeySecurityClient(registrationId, symmetricKey);
  var provisioningClient = ProvisioningDeviceClient.create(provisioningHost, idScope, new ProvProtocol(), provSecurityClient);

  if (payload) {
    provisioningClient.setProvisioningPayload(payload);
  }

  try {
    let result = await provisioningClient.register();
    deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';SharedAccessKey=' + symmetricKey;
    console.log('registration succeeded');
    console.log('assigned hub=' + result.assignedHub);
    console.log('deviceId=' + result.deviceId);
    console.log('payload=' + JSON.stringify(result.payload));
  } catch (err) {
    console.error("error registering device: " + err.toString());
  }
}

De functie sendTelemetry laat zien hoe het apparaat de temperatuurtelemetriegegevens verzendt naar IoT Central. Voor telemetrie van onderdelen wordt een eigenschap toegevoegd die wordt aangeroepen $.sub met de naam van het onderdeel:

async function sendTelemetry(deviceClient, data, index, componentName) {
  if componentName) {
    console.log('Sending telemetry message %d from component: %s ', index, componentName);
  } else {
    console.log('Sending telemetry message %d from root interface', index);
  }
  const msg = new Message(data);
  if (componentName) {
    msg.properties.add(messageSubjectProperty, componentName);
  }
  msg.contentType = 'application/json';
  msg.contentEncoding = 'utf-8';
  await deviceClient.sendEvent(msg);
}

De main methode maakt gebruik van een helpermethode die wordt aangeroepen helperCreateReportedPropertiesPatch om berichten voor het bijwerken van eigenschappen te maken. Deze methode gebruikt een optionele parameter om het onderdeel op te geven dat de eigenschap verzendt:

const helperCreateReportedPropertiesPatch = (propertiesToReport, componentName) => {
  let patch;
  if (!!(componentName)) {
    patch = { };
    propertiesToReport.__t = 'c';
    patch[componentName] = propertiesToReport;
  } else {
    patch = { };
    patch = propertiesToReport;
  }
  if (!!(componentName)) {
    console.log('The following properties will be updated for component: ' + componentName);
  } else {
    console.log('The following properties will be updated for root interface.');
  }
  console.log(patch);
  return patch;
};

De main methode gebruikt de volgende methode om updates te verwerken voor schrijfbare eigenschappen van IoT Central. U ziet hoe de methode het antwoord bouwt met de versie en statuscode:

const desiredPropertyPatchListener = (deviceTwin, componentNames) => {
  deviceTwin.on('properties.desired', (delta) => {
    console.log('Received an update for device with value: ' + JSON.stringify(delta));
    Object.entries(delta).forEach(([key, values]) => {
      const version = delta.$version;
      if (!!(componentNames) && componentNames.includes(key)) { // then it is a component we are expecting
        const componentName = key;
        const patchForComponents = { [componentName]: {} };
        Object.entries(values).forEach(([propertyName, propertyValue]) => {
          if (propertyName !== '__t' && propertyName !== '$version') {
            console.log('Will update property: ' + propertyName + ' to value: ' + propertyValue + ' of component: ' + componentName);
            const propertyContent = { value: propertyValue };
            propertyContent.ac = 200;
            propertyContent.ad = 'Successfully executed patch';
            propertyContent.av = version;
            patchForComponents[componentName][propertyName] = propertyContent;
          }
        });
        updateComponentReportedProperties(deviceTwin, patchForComponents, componentName);
      }
      else if  (key !== '$version') { // individual property for root
        const patchForRoot = { };
        console.log('Will update property: ' + key + ' to value: ' + values + ' for root');
        const propertyContent = { value: values };
        propertyContent.ac = 200;
        propertyContent.ad = 'Successfully executed patch';
        propertyContent.av = version;
        patchForRoot[key] = propertyContent;
        updateComponentReportedProperties(deviceTwin, patchForRoot, null);
      }
    });
  });
};

De main methode gebruikt de volgende methoden om opdrachten van IoT Central af te handelen:

const commandHandler = async (request, response) => {
  helperLogCommandRequest(request);
  switch (request.methodName) {
  case commandNameGetMaxMinReport1: {
    await sendCommandResponse(request, response, 200, thermostat1.getMaxMinReportObject());
    break;
  }
  case commandNameGetMaxMinReport2: {
    await sendCommandResponse(request, response, 200, thermostat2.getMaxMinReportObject());
    break;
  }
  case commandNameReboot: {
    await sendCommandResponse(request, response, 200, 'reboot response');
    break;
  }
  default:
    await sendCommandResponse(request, response, 404, 'unknown method');
    break;
  }
};

const sendCommandResponse = async (request, response, status, payload) => {
  try {
    await response.send(status, payload);
    console.log('Response to method: ' + request.methodName + ' sent successfully.' );
  } catch (err) {
    console.error('An error occurred when sending a method response:\n' + err.toString());
  }
};

Verbindingsgegevens ophalen

Wanneer u de toepassing voor het voorbeeldapparaat later in deze zelfstudie uitvoert, hebt u de volgende configuratiewaarden nodig:

  • Id-bereik: Navigeer in uw IoT Central-toepassing naar verbindingsgroepen voor machtigingen>. Noteer de waarde van Id-bereik.
  • Primaire sleutel groeperen: Navigeer in uw IoT Central-toepassing naar Verbindingsgroepen > voor machtigingen > voor SAS-IoT-Devices. Noteer de waarde van Primaire sleutel onder Shared Access Signature (SAS).

Gebruik Azure Cloud Shell om een apparaatsleutel te genereren op basis van de primaire sleutel van de groep die u hebt opgehaald:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Noteer de gegenereerde apparaatsleutel. U hebt deze waarde verderop in deze zelfstudie nodig.

Notitie

Als u dit voorbeeld wilt uitvoeren, hoeft u het apparaat niet vooraf te registreren in uw IoT Central-toepassing. In het voorbeeld wordt de IoT Central-mogelijkheid gebruikt om apparaten automatisch te registreren wanneer ze voor het eerst verbinding maken.

De code uitvoeren

Als u de voorbeeldtoepassing wilt uitvoeren, opent u een opdrachtregelomgeving en navigeert u naar de map azure-iot-sdk-node/device/samples/javascript-map die het pnp_temperature_controller.js voorbeeldbestand bevat.

Stel de omgevingsvariabelen in om het voorbeeld te configureren. Het volgende codefragment laat zien hoe u de omgevingsvariabelen instelt vanaf de Windows-opdrachtprompt. Als u een bash-shell gebruikt, vervangt u de set-opdrachten door export-opdrachten:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Installeer de vereiste pakketten:

npm install

Voer het voorbeeld uit:

node pnp_temperature_controller.js

In de volgende uitvoer ziet u hoe het apparaat zich registreert bij IoT Central en er verbinding mee maakt. Het voorbeeld verzendt vervolgens de maxTempSinceLastReboot eigenschap van de twee thermostaatonderdelen voordat de telemetrie wordt verzonden:

registration succeeded
assigned hub=iotc-....azure-devices.net
deviceId=sample-device-01
payload=undefined
Connecting using connection string: HostName=iotc-....azure-devices.net;DeviceId=sample-device-01;SharedAccessKey=qdv...IpAo=
Enabling the commands on the client
Please enter q or Q to exit sample.
The following properties will be updated for root interface.
{ serialNumber: 'alwinexlepaho8329' }
The following properties will be updated for component: thermostat1
{ thermostat1: { maxTempSinceLastReboot: 1.5902294191855972, __t: 'c' } }
The following properties will be updated for component: thermostat2
{ thermostat2: { maxTempSinceLastReboot: 16.181771928614545, __t: 'c' } }
The following properties will be updated for component: deviceInformation
{ deviceInformation:
   { manufacturer: 'Contoso Device Corporation',
     model: 'Contoso 47-turbo',
     swVersion: '10.89',
     osName: 'Contoso_OS',
     processorArchitecture: 'Contoso_x86',
     processorManufacturer: 'Contoso Industries',
     totalStorage: 65000,
     totalMemory: 640,
     __t: 'c' } }
executed sample
Received an update for device with value: {"$version":1}
Properties have been reported for component: thermostat1
Properties have been reported for component: thermostat2
Properties have been reported for component: deviceInformation
Properties have been reported for root interface.
Sending telemetry message 0 from component: thermostat1 
Sending telemetry message 0 from component: thermostat2 
Sending telemetry message 0 from root interface

Als operator in uw Azure IoT Central-toepassing kunt u:

  • Bekijk de telemetrie die is verzonden door de twee thermostaatonderdelen op de pagina Overzicht :

    Screenshot that shows the device overview page.

  • Bekijk de apparaateigenschappen op de pagina Info . Op deze pagina worden de eigenschappen van het apparaatinformatieonderdeel en de twee thermostaatonderdelen weergegeven:

    Screenshot that shows the device properties view.

Het apparaatsjabloon aanpassen

Als oplossingsontwikkelaar kunt u de apparaatsjabloon aanpassen die door IoT Central automatisch wordt gemaakt wanneer het apparaat voor de temperatuurcontroller is verbonden.

Als u een cloudeigenschap wilt toevoegen om de naam van de klant op te slaan die aan het apparaat is gekoppeld:

  1. Navigeer in uw IoT Central-toepassing naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Selecteer in het model Temperatuurregelaar de optie +Mogelijkheid toevoegen.

  3. Voer de naam van de klant in als weergavenaam, selecteer cloudeigenschap als het mogelijkheidstype, vouw de vermelding uit en kies Tekenreeks als het schema. Selecteer vervolgens Opslaan.

Ga als volgt te werk om aan te passen hoe de rapportopdrachten Max-Min ophalen worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Vervang voor getMaxMinReport (thermostat1) het rapport Max-Min ophalen doorhet statusrapport thermostaat1 ophalen.

  3. Vervang voor getMaxMinReport (thermostat2) het rapport Max-Min ophalen doorhet statusrapport Thermostaat2 ophalen.

  4. Selecteer Opslaan.

U kunt als volgt aanpassen hoe de beschrijfbare eigenschappen van de doeltemperatuur worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Voor targetTemperature (thermostaat1) vervangt u de doeltemperatuur door doeltemperatuur (1).

  3. Voor targetTemperature (thermostaat2) vervangt u doeltemperatuur door doeltemperatuur (2).

  4. Selecteer Opslaan.

De thermostaatonderdelen in het model temperatuurcontroller bevatten de beschrijfbare eigenschap Doeltemperatuur , de apparaatsjabloon bevat de cloudeigenschap Klantnaam . Een weergave maken die een operator kan gebruiken om deze eigenschappen te bewerken:

  1. Selecteer Weergaven en vervolgens de tegel Apparaat- en cloudgegevens bewerken.

  2. Voer Eigenschappen in als de formuliernaam.

  3. Selecteer de eigenschappen Doeltemperatuur (1), Doeltemperatuur (2) en Klantnaam . Selecteer vervolgens Sectie toevoegen.

  4. Sla uw wijzigingen op.

Screenshot that shows a view for updating property values.

De apparaatsjabloon publiceren

Voordat een operator de aanpassingen kan zien en gebruiken die u hebt gemaakt, moet u de apparaatsjabloon publiceren.

Selecteer In de apparaatsjabloonThermostaat de optie Publiceren. Selecteer Publiceren in het deelvenster Dit apparaatsjabloon op de toepassing publiceren.

Een operator kan nu de weergave Eigenschappen gebruiken om de eigenschapswaarden bij te werken en opdrachten aan te roepen met de naam Statusrapport Thermostaat1 ophalen en Thermostaat2-statusrapport ophalen op de pagina met apparaatopdrachten:

  • Schrijfbare eigenschapswaarden bijwerken op de pagina Eigenschappen :

    Screenshot that shows updating the device properties.

  • Roep de opdrachten aan vanaf de pagina Opdrachten . Als u de opdracht statusrapport uitvoert, selecteert u een datum en tijd voor de parameter Sinds voordat u deze uitvoert:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

U kunt zien hoe het apparaat reageert op opdrachten en updates van eigenschappen. De getMaxMinReport opdracht bevindt zich in het thermostat2 onderdeel, de reboot opdracht bevindt zich in het standaardonderdeel. De targetTemperature beschrijfbare eigenschap is ingesteld voor het thermostat2 onderdeel:

Received command request for command name: thermostat2*getMaxMinReport
The command request payload is:
2021-03-26T06:00:00.000Z
Response to method: thermostat2*getMaxMinReport sent successfully.

...

Received command request for command name: reboot
The command request payload is:
10
Response to method: reboot sent successfully.

...

Received an update for device with value: {"thermostat2":{"targetTemperature":76,"__t":"c"},"$version":2}
Will update property: targetTemperature to value: 76 of component: thermostat2
Properties have been reported for component: thermostat2

Browse code

Vereisten

Als u de stappen in dit artikel wilt uitvoeren, hebt u de volgende resources nodig:

  • Een ontwikkelcomputer waarop Python is geïnstalleerd. Controleer de Azure IoT Python SDK op de huidige python-versievereisten. Voer python --version uit op de opdrachtprompt om uw versie te controleren. Python is beschikbaar voor een groot aantal verschillende besturingssystemen. In de instructies in deze zelfstudie wordt ervan uitgegaan dat u de opdracht python uitvoert vanaf de Windows-opdrachtprompt.

  • Een lokale kopie van de Microsoft Azure IoT-SDK voor Python GitHub-opslagplaats die de voorbeeldcode bevat. Gebruik deze koppeling om een kopie van de opslagplaats te downloaden: ZIP downloaden. Pak het bestand vervolgens uit op een geschikte locatie op uw lokale computer.

De code bekijken

Open in de kopie van de Microsoft Azure IoT SDK voor Python die u eerder hebt gedownload het bestand azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py in een teksteditor.

In het voorbeeld wordt het digital twin definition Language-model met meerdere onderdelen geïmplementeerd.

Wanneer u het voorbeeld uitvoert om verbinding te maken met IoT Central, wordt daarvoor gebruikgemaakt van de Device Provisioning Service (DPS) om het apparaat te registreren en een verbindingsreeks te genereren. Het voorbeeld haalt de DPS-verbindingsgegevens die nodig zijn op uit de opdrachtregelomgeving.

De functie main doet het volgende:

  • Gebruikt DPS om het apparaat in te richten. De inrichtingsgegevens bevatten de model-id. IoT Central gebruikt de model-id om de apparaatsjabloon voor dit apparaat te identificeren of te genereren. Zie Een apparaat toewijzen aan een apparaatsjabloon voor meer informatie.
  • Hiermee wordt een Device_client-object gemaakt en wordt de id van het dtmi:com:example:TemperatureController;2-model ingesteld voordat de verbinding wordt geopend.
  • Verzendt initiële eigenschapswaarden naar IoT Central. Het maakt gebruik van de pnp_helper patches.
  • Hiermee maakt u listeners voor de getMaxMinReport en reboot opdrachten. Elk thermostaatonderdeel heeft een eigen getMaxMinReport opdracht.
  • Maakt de eigenschap listener om te luisteren naar updates van beschrijfbare eigenschappen.
  • Start een lus voor het verzenden van temperatuurtelemetrie van de twee thermostaatonderdelen en het werksettelemetrie van het standaardonderdeel om de 8 seconden.
async def main():
    switch = os.getenv("IOTHUB_DEVICE_SECURITY_TYPE")
    if switch == "DPS":
        provisioning_host = (
            os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            if os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            else "global.azure-devices-provisioning.net"
        )
        id_scope = os.getenv("IOTHUB_DEVICE_DPS_ID_SCOPE")
        registration_id = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID")
        symmetric_key = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_KEY")

        registration_result = await provision_device(
            provisioning_host, id_scope, registration_id, symmetric_key, model_id
        )

        if registration_result.status == "assigned":
            print("Device was assigned")
            print(registration_result.registration_state.assigned_hub)
            print(registration_result.registration_state.device_id)
            device_client = IoTHubDeviceClient.create_from_symmetric_key(
                symmetric_key=symmetric_key,
                hostname=registration_result.registration_state.assigned_hub,
                device_id=registration_result.registration_state.device_id,
                product_info=model_id,
            )
        else:
            raise RuntimeError(
                "Could not provision device. Aborting Plug and Play device connection."
            )

    elif switch == "connectionString":
        # ...

    # Connect the client.
    await device_client.connect()

    ################################################
    # Update readable properties from various components

    properties_root = pnp_helper.create_reported_properties(serialNumber=serial_number)
    properties_thermostat1 = pnp_helper.create_reported_properties(
        thermostat_1_component_name, maxTempSinceLastReboot=98.34
    )
    properties_thermostat2 = pnp_helper.create_reported_properties(
        thermostat_2_component_name, maxTempSinceLastReboot=48.92
    )
    properties_device_info = pnp_helper.create_reported_properties(
        device_information_component_name,
        swVersion="5.5",
        manufacturer="Contoso Device Corporation",
        model="Contoso 4762B-turbo",
        osName="Mac Os",
        processorArchitecture="x86-64",
        processorManufacturer="Intel",
        totalStorage=1024,
        totalMemory=32,
    )

    property_updates = asyncio.gather(
        device_client.patch_twin_reported_properties(properties_root),
        device_client.patch_twin_reported_properties(properties_thermostat1),
        device_client.patch_twin_reported_properties(properties_thermostat2),
        device_client.patch_twin_reported_properties(properties_device_info),
    )

    ################################################
    # Get all the listeners running
    print("Listening for command requests and property updates")

    global THERMOSTAT_1
    global THERMOSTAT_2
    THERMOSTAT_1 = Thermostat(thermostat_1_component_name, 10)
    THERMOSTAT_2 = Thermostat(thermostat_2_component_name, 10)

    listeners = asyncio.gather(
        execute_command_listener(
            device_client, method_name="reboot", user_command_handler=reboot_handler
        ),
        execute_command_listener(
            device_client,
            thermostat_1_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_command_listener(
            device_client,
            thermostat_2_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_property_listener(device_client),
    )

    ################################################
    # Function to send telemetry every 8 seconds

    async def send_telemetry():
        print("Sending telemetry from various components")

        while True:
            curr_temp_ext = random.randrange(10, 50)
            THERMOSTAT_1.record(curr_temp_ext)

            temperature_msg1 = {"temperature": curr_temp_ext}
            await send_telemetry_from_temp_controller(
                device_client, temperature_msg1, thermostat_1_component_name
            )

            curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius
            THERMOSTAT_2.record(curr_temp_int)

            temperature_msg2 = {"temperature": curr_temp_int}

            await send_telemetry_from_temp_controller(
                device_client, temperature_msg2, thermostat_2_component_name
            )

            workingset_msg3 = {"workingSet": random.randrange(1, 100)}
            await send_telemetry_from_temp_controller(device_client, workingset_msg3)

    send_telemetry_task = asyncio.ensure_future(send_telemetry())

    # ...

De functie provision_device gebruikt DPS om het apparaat in te richten en te registreren bij IoT Central. De functie bevat de apparaatmodel-id, die IoT Central gebruikt om een apparaat toe te wijzen aan een apparaatsjabloon, in de inrichtingspayload:

async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
        provisioning_host=provisioning_host,
        registration_id=registration_id,
        id_scope=id_scope,
        symmetric_key=symmetric_key,
    )

    provisioning_device_client.provisioning_payload = {"modelId": model_id}
    return await provisioning_device_client.register()

De execute_command_listener functie verwerkt opdrachtaanvragen, voert de max_min_handler functie uit wanneer het apparaat de getMaxMinReport opdracht voor de thermostaatonderdelen ontvangt en de reboot_handler functie wanneer het apparaat de reboot opdracht ontvangt. De module maakt gebruik van de pnp_helper module om het antwoord te bouwen:

async def execute_command_listener(
    device_client,
    component_name=None,
    method_name=None,
    user_command_handler=None,
    create_user_response_handler=None,
):
    while True:
        if component_name and method_name:
            command_name = component_name + "*" + method_name
        elif method_name:
            command_name = method_name
        else:
            command_name = None

        command_request = await device_client.receive_method_request(command_name)
        print("Command request received with payload")
        values = command_request.payload
        print(values)

        if user_command_handler:
            await user_command_handler(values)
        else:
            print("No handler provided to execute")

        (response_status, response_payload) = pnp_helper.create_response_payload_with_status(
            command_request, method_name, create_user_response=create_user_response_handler
        )

        command_response = MethodResponse.create_from_method_request(
            command_request, response_status, response_payload
        )

        try:
            await device_client.send_method_response(command_response)
        except Exception:
            print("responding to the {command} command failed".format(command=method_name))

De async def execute_property_listener handles beschrijfbare eigenschapsupdates, zoals targetTemperature voor de thermostaatonderdelen en genereren het JSON-antwoord. De module maakt gebruik van de pnp_helper module om het antwoord te bouwen:

async def execute_property_listener(device_client):
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call
        print(patch)
        properties_dict = pnp_helper.create_reported_properties_from_desired(patch)

        await device_client.patch_twin_reported_properties(properties_dict)

De send_telemetry_from_temp_controller functie verzendt de telemetrieberichten van de thermostaatonderdelen naar IoT Central. De module maakt gebruik van de pnp_helper module om de berichten te bouwen:

async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = pnp_helper.create_telemetry(telemetry_msg, component_name)
    await device_client.send_message(msg)
    print("Sent message")
    print(msg)
    await asyncio.sleep(5)

Verbindingsgegevens ophalen

Wanneer u de toepassing voor het voorbeeldapparaat later in deze zelfstudie uitvoert, hebt u de volgende configuratiewaarden nodig:

  • Id-bereik: Navigeer in uw IoT Central-toepassing naar verbindingsgroepen voor machtigingen>. Noteer de waarde van Id-bereik.
  • Primaire sleutel groeperen: Navigeer in uw IoT Central-toepassing naar Verbindingsgroepen > voor machtigingen > voor SAS-IoT-Devices. Noteer de waarde van Primaire sleutel onder Shared Access Signature (SAS).

Gebruik Azure Cloud Shell om een apparaatsleutel te genereren op basis van de primaire sleutel van de groep die u hebt opgehaald:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Noteer de gegenereerde apparaatsleutel. U hebt deze waarde verderop in deze zelfstudie nodig.

Notitie

Als u dit voorbeeld wilt uitvoeren, hoeft u het apparaat niet vooraf te registreren in uw IoT Central-toepassing. In het voorbeeld wordt de IoT Central-mogelijkheid gebruikt om apparaten automatisch te registreren wanneer ze voor het eerst verbinding maken.

De code uitvoeren

Als u de voorbeeldtoepassing wilt uitvoeren, opent u een opdrachtregelomgeving en navigeert u naar de map azure-iot-sdk-python-2/samples/pnp die het temp_controller_with_thermostats.py voorbeeldbestand bevat.

Stel de omgevingsvariabelen in om het voorbeeld te configureren. Het volgende codefragment laat zien hoe u de omgevingsvariabelen instelt vanaf de Windows-opdrachtprompt. Als u een bash-shell gebruikt, vervangt u de set-opdrachten door export-opdrachten:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Installeer de vereiste pakketten:

pip install azure-iot-device

Voer het voorbeeld uit:

python temp_controller_with_thermostats.py

In de volgende uitvoer ziet u hoe het apparaat zich registreert bij IoT Central en er verbinding mee maakt. Het voorbeeld verzendt de maxTempSinceLastReboot eigenschappen van de twee thermostaatonderdelen voordat telemetrie wordt verzonden:

Device was assigned
iotc-60a.....azure-devices.net
sample-device-01
Updating pnp properties for root interface
{'serialNumber': 'alohomora'}
Updating pnp properties for thermostat1
{'thermostat1': {'maxTempSinceLastReboot': 98.34, '__t': 'c'}}
Updating pnp properties for thermostat2
{'thermostat2': {'maxTempSinceLastReboot': 48.92, '__t': 'c'}}
Updating pnp properties for deviceInformation
{'deviceInformation': {'swVersion': '5.5', 'manufacturer': 'Contoso Device Corporation', 'model': 'Contoso 4762B-turbo', 'osName': 'Mac Os', 'processorArchitecture': 'x86-64', 'processorManufacturer': 'Intel', 'totalStorage': 1024, 'totalMemory': 32, '__t': 'c'}}
Listening for command requests and property updates
Press Q to quit
Sending telemetry from various components
Sent message
{"temperature": 27}
Sent message
{"temperature": 17}
Sent message
{"workingSet": 13}

Als operator in uw Azure IoT Central-toepassing kunt u:

  • Bekijk de telemetrie die is verzonden door de twee thermostaatonderdelen op de pagina Overzicht :

    Screenshot that shows the device overview page.

  • Bekijk de apparaateigenschappen op de pagina Info . Op deze pagina worden de eigenschappen van het apparaatinformatieonderdeel en de twee thermostaatonderdelen weergegeven:

    Screenshot that shows the device properties view.

Het apparaatsjabloon aanpassen

Als oplossingsontwikkelaar kunt u de apparaatsjabloon aanpassen die door IoT Central automatisch wordt gemaakt wanneer het apparaat voor de temperatuurcontroller is verbonden.

Als u een cloudeigenschap wilt toevoegen om de naam van de klant op te slaan die aan het apparaat is gekoppeld:

  1. Navigeer in uw IoT Central-toepassing naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Selecteer in het model Temperatuurregelaar de optie +Mogelijkheid toevoegen.

  3. Voer de naam van de klant in als weergavenaam, selecteer cloudeigenschap als het mogelijkheidstype, vouw de vermelding uit en kies Tekenreeks als het schema. Selecteer vervolgens Opslaan.

Ga als volgt te werk om aan te passen hoe de rapportopdrachten Max-Min ophalen worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Vervang voor getMaxMinReport (thermostat1) het rapport Max-Min ophalen doorhet statusrapport thermostaat1 ophalen.

  3. Vervang voor getMaxMinReport (thermostat2) het rapport Max-Min ophalen doorhet statusrapport Thermostaat2 ophalen.

  4. Selecteer Opslaan.

U kunt als volgt aanpassen hoe de beschrijfbare eigenschappen van de doeltemperatuur worden weergegeven in uw IoT Central-toepassing:

  1. Navigeer naar de apparaatsjabloon Temperatuurcontroller op de pagina Apparaatsjablonen .

  2. Voor targetTemperature (thermostaat1) vervangt u de doeltemperatuur door doeltemperatuur (1).

  3. Voor targetTemperature (thermostaat2) vervangt u doeltemperatuur door doeltemperatuur (2).

  4. Selecteer Opslaan.

De thermostaatonderdelen in het model temperatuurcontroller bevatten de beschrijfbare eigenschap Doeltemperatuur , de apparaatsjabloon bevat de cloudeigenschap Klantnaam . Een weergave maken die een operator kan gebruiken om deze eigenschappen te bewerken:

  1. Selecteer Weergaven en vervolgens de tegel Apparaat- en cloudgegevens bewerken.

  2. Voer Eigenschappen in als de formuliernaam.

  3. Selecteer de eigenschappen Doeltemperatuur (1), Doeltemperatuur (2) en Klantnaam . Selecteer vervolgens Sectie toevoegen.

  4. Sla uw wijzigingen op.

Screenshot that shows a view for updating property values.

De apparaatsjabloon publiceren

Voordat een operator de aanpassingen kan zien en gebruiken die u hebt gemaakt, moet u de apparaatsjabloon publiceren.

Selecteer In de apparaatsjabloonThermostaat de optie Publiceren. Selecteer Publiceren in het deelvenster Dit apparaatsjabloon op de toepassing publiceren.

Een operator kan nu de weergave Eigenschappen gebruiken om de eigenschapswaarden bij te werken en opdrachten aan te roepen met de naam Statusrapport Thermostaat1 ophalen en Thermostaat2-statusrapport ophalen op de pagina met apparaatopdrachten:

  • Schrijfbare eigenschapswaarden bijwerken op de pagina Eigenschappen :

    Screenshot that shows updating the device properties.

  • Roep de opdrachten aan vanaf de pagina Opdrachten . Als u de opdracht statusrapport uitvoert, selecteert u een datum en tijd voor de parameter Sinds voordat u deze uitvoert:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

U kunt zien hoe het apparaat reageert op opdrachten en updates van eigenschappen:

{'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
the data in the desired properties patch was: {'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
Values received are :-
{'targetTemperature': 67, '__t': 'c'}
Sent message

...

Command request received with payload
2021-03-31T05:00:00.000Z
Will return the max, min and average temperature from the specified time 2021-03-31T05:00:00.000Z to the current time
Done generating
{"avgTemp": 4.0, "endTime": "2021-03-31T12:29:48.322427", "maxTemp": 18, "minTemp": null, "startTime": "2021-03-31T12:28:28.322381"}

Onbewerkte gegevens weergeven

U kunt de weergave Onbewerkte gegevens gebruiken om de onbewerkte gegevens te onderzoeken die uw apparaat verzendt naar IoT Central:

Screenshot that shows the raw data view.

In deze weergave kunt u de kolommen selecteren die u wilt weergeven en een tijdsbereik instellen. In de kolom Niet-gemodelleerde gegevens worden apparaatgegevens weergegeven die niet overeenkomen met een eigenschap of telemetriedefinities in de apparaatsjabloon.

Resources opschonen

Als u geen verdere quickstarts of zelfstudies voor IoT Central wilt voltooien, kunt u uw IoT Central-toepassing verwijderen:

  1. Navigeer in uw IoT Central-toepassing naar Toepassingsbeheer>.
  2. Selecteer Verwijderen en bevestig uw actie.

Volgende stappen

Als u liever wilt doorgaan met de set zelfstudies over IoT Central en meer wilt leren over het bouwen van een IoT Central-oplossing, raadpleegt u: