Sdílet prostřednictvím


Kurz: Vytvoření a připojení klientské aplikace k aplikaci Azure IoT Central

Důležité

Tento článek obsahuje postup připojení zařízení pomocí sdíleného přístupového podpisu, označovaného také jako ověřování symetrického klíče. Tato metoda ověřování je vhodná pro testování a vyhodnocení, ale ověřování zařízení pomocí certifikátů X.509 je bezpečnější přístup. Další informace najdete v tématu Zabezpečení osvědčených postupů > zabezpečení připojení.

V tomto kurzu se dozvíte, jak připojit klientskou aplikaci k aplikaci Azure IoT Central. Aplikace simuluje chování zařízení řadiče teploty. Když se aplikace připojí ke službě IoT Central, odešle ID modelu zařízení kontroleru teploty. IoT Central používá ID modelu k načtení modelu zařízení a vytvoření šablony zařízení za vás. Do šablony zařízení přidáte zobrazení, abyste operátorovi umožnili interakci se zařízením.

V tomto kurzu se naučíte:

  • Vytvořte a spusťte kód zařízení a podívejte se na něj, jak se připojí k aplikaci IoT Central.
  • Zobrazte simulovaná telemetrická data odeslaná ze zařízení.
  • Přidání vlastních zobrazení do šablony zařízení
  • Publikujte šablonu zařízení.
  • Pomocí zobrazení můžete spravovat vlastnosti zařízení.
  • Zavolejte příkaz pro řízení zařízení.

Procházet kód

Požadavky

K dokončení kroků v tomto kurzu potřebujete:

Tento kurz můžete spustit v Linuxu nebo Windows. Příkazy prostředí v tomto kurzu se řídí konvencí Linuxu pro oddělovače/ cest, pokud postupujete společně s Windows, nezapomeňte tyto oddělovače prohodit za "\".

Požadavky se liší podle operačního systému:

Linux

V tomto kurzu se předpokládá, že používáte Ubuntu Linux. Kroky v tomto kurzu byly testovány pomocí Ubuntu 18.04.

Pokud chcete tento kurz dokončit v Linuxu, nainstalujte do místního linuxového prostředí následující software:

Pomocí příkazu nainstalujte GCC, Git, cmake a všechny požadované závislosti apt-get :

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

Ověřte, že je verze cmake větší než 2.8.12 a verze GCC je větší než 4.4.7.

cmake --version
gcc --version

Windows

Chcete-li dokončit tento kurz ve Windows, nainstalujte do místního prostředí Windows následující software:

Stáhněte si kód

V tomto kurzu připravíte vývojové prostředí, které můžete použít ke klonování a sestavení sady AZURE IoT Hub Device C SDK.

Otevřete příkazový řádek v adresáři podle vašeho výběru. Spuštěním následujícího příkazu naklonujte úložiště GitHub sdk a knihoven Azure IoT C do tohoto umístění:

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

Očekáváme, že dokončení této operace bude trvat několik minut.

Kontrola kódu

V kopii sady Microsoft Azure IoT SDK pro jazyk C, kterou jste si stáhli dříve, otevřete soubory azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c a azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c v textovém editoru.

Ukázka implementuje model více komponent Temperature Controller Digital Twin Definition Language.

Když spustíte ukázku pro připojení k IoT Central, zaregistruje zařízení pomocí služby Device Provisioning Service (DPS) a vygeneruje připojovací řetězec. Ukázka načte informace o připojení DPS, které potřebuje, z prostředí příkazového řádku.

V pnp_temperature_controller.cmain funkce nejprve voláCreateDeviceClientAndAllocateComponents:

  • dtmi:com:example:Thermostat;1 Nastavte ID modelu. IoT Central používá ID modelu k identifikaci nebo vygenerování šablony zařízení pro toto zařízení. Další informace najdete v tématu Přiřazení zařízení k šabloně zařízení.
  • Pomocí DPS zřiďte a zaregistrujte zařízení.
  • Vytvořte popisovač klienta zařízení a připojte se k aplikaci IoT Central.
  • Vytvoří obslužnou rutinu pro příkazy v komponentě kontroleru teploty.
  • Vytvoří obslužnou rutinu pro aktualizace vlastností v komponentě kontroleru teploty.
  • Vytvoří dvě součásti termostatu.

Další main funkce:

  • Hlásí některé počáteční hodnoty vlastností pro všechny komponenty.
  • Spustí smyčku pro odesílání telemetrie ze všech komponent.

Funkce main pak spustí vlákno, které bude pravidelně odesílat telemetrii.

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

Funkce ukazujepnp_thermostat_component.cPnP_ThermostatComponent_SendCurrentTemperature, jak zařízení odesílá telemetrii teploty ze komponenty do 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);
}

Funkce pnp_thermostat_component.codešle PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property maxTempSinceLastReboot aktualizaci vlastnosti ze komponenty do 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);
    }
}

PnP_ThermostatComponent_ProcessPropertyUpdate Funkce pnp_thermostat_component.czpracovává aktualizace zapisovatelných vlastností z IoT Central:

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

PnP_ThermostatComponent_ProcessCommand Funkce pnp_thermostat_component.czpracovává příkazy volané z 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;
    }
}

Sestavení kódu

Sadu SDK zařízení použijete k sestavení zahrnutého ukázkového kódu:

  1. Vytvořte podadresář cmake v kořenové složce sady SDK zařízení a přejděte do této složky:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Spuštěním následujících příkazů sestavte sadu SDK a ukázky:

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

Získání informací o připojení

Když později v tomto kurzu spustíte ukázkovou aplikaci zařízení, budete potřebovat následující konfigurační hodnoty:

  • Rozsah ID: V aplikaci IoT Central přejděte do skupin připojení zařízení oprávnění>. Poznamenejte si hodnotu oboru ID.
  • Primární klíč skupiny: V aplikaci IoT Central přejděte na Skupiny > připojení zařízení OPRÁVNĚNÍ > SAS-IoT-Devices. Poznamenejte si hodnotu primárního klíče sdíleného přístupového podpisu.

Pomocí Azure Cloud Shellu vygenerujte klíč zařízení z primárního klíče skupiny, který jste získali:

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

Poznamenejte si vygenerovaný klíč zařízení, který použijete později v tomto kurzu.

Poznámka:

Pokud chcete tuto ukázku spustit, nemusíte zařízení předem zaregistrovat ve své aplikaci IoT Central. Ukázka používá funkci IoT Central k automatické registraci zařízení při prvním připojení.

Spuštění kódu

Pokud chcete spustit ukázkovou aplikaci, otevřete prostředí příkazového řádku a přejděte do složky azure-iot-sdk-c\cmake.

Nastavte proměnné prostředí pro konfiguraci ukázky. Následující fragment kódu ukazuje, jak nastavit proměnné prostředí na příkazovém řádku Windows. Pokud používáte prostředí Bash , nahraďte set příkazy export příkazy:

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

Spuštění ukázky:

# 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

Následující výstup ukazuje registraci zařízení a připojení k IoT Central. Ukázka začne odesílat telemetrii:

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

Jako operátor ve vaší aplikaci Azure IoT Central můžete:

  • Zobrazte telemetrii odesílanou dvěma komponentami termostatu na stránce Přehled :

    Snímek obrazovky znázorňující stránku s přehledem zařízení

  • Zobrazte vlastnosti zařízení na stránce O produktu. Tato stránka zobrazuje vlastnosti ze komponenty informací o zařízení a dvě komponenty termostatu:

    Snímek obrazovky znázorňující zobrazení vlastností zařízení

Přizpůsobení šablony zařízení

Jako vývojář řešení můžete přizpůsobit šablonu zařízení, kterou Služba IoT Central vytvořila automaticky při připojení zařízení kontroleru teploty.

Přidání cloudové vlastnosti pro uložení jména zákazníka přidruženého k zařízení:

  1. V aplikaci IoT Central přejděte na šablonu zařízení Teplotní kontroler na stránce Šablony zařízení.

  2. V modelu Teplotní kontroler vyberte +Přidat schopnost.

  3. Jako zobrazovaný název zadejte jméno zákazníka, jako typ schopnosti vyberte vlastnost Cloud, rozbalte položku a zvolte Řetězec jako schéma. Pak vyberte Uložit.

Přizpůsobení zobrazení příkazů sestavy Get Max-Min v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. V případě getMaxMinReport (termostat1) nahraďte sestavu Get Max-Min. Získáte termostat1 stavovou zprávu.

  3. V případě getMaxMinReport (termostat2) nahraďte sestavu Get Max-Min. Získáte zprávu o stavu termostat2.

  4. Zvolte Uložit.

Přizpůsobení způsobu zobrazení zapisovatelných vlastností cílové teploty v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. Pro targetTemperature (termostat1) nahraďte cílovou teplotu cílovou teplotou (1).

  3. Pro targetTemperature (termostat2) nahraďte cílovou teplotu cílovou teplotou (2).

  4. Zvolte Uložit.

Součástí termostatu v modelu kontroleru teploty je vlastnost Zapisovatelná cílová teplota , šablona zařízení obsahuje cloudovou vlastnost Customer Name . Vytvořte zobrazení, které může operátor použít k úpravě těchto vlastností:

  1. Vyberte Zobrazení a pak vyberte dlaždici Pro úpravy zařízení a cloudových dat .

  2. Jako název formuláře zadejte Vlastnosti .

  3. Vyberte vlastnosti Cílová teplota (1), Cílová teplota (2) a Jméno zákazníka. Pak vyberte Přidat oddíl.

  4. Uložte provedené změny.

Snímek obrazovky znázorňující zobrazení pro aktualizaci hodnot vlastností

Publikování šablony zařízení

Než operátor uvidí a použije vlastní nastavení, které jste provedli, musíte šablonu zařízení publikovat.

V šabloně termostatu vyberte Publikovat. Na panelu aplikace vyberte Publikovat tuto šablonu zařízení.

Operátor teď může pomocí zobrazení Vlastnosti aktualizovat hodnoty vlastností a volat příkazy s názvem Get termostat1 status report and Get termostat2 status report on the device commands page:

  • Aktualizujte zapisovatelné hodnoty vlastností na stránce Vlastnosti :

    Snímek obrazovky znázorňující aktualizaci vlastností zařízení

  • Volání příkazů ze stránky Příkazy Pokud spustíte příkaz zprávy o stavu, vyberte před spuštěním parametru Since datum a čas:

    Snímek obrazovky znázorňující volání příkazu

    Snímek obrazovky znázorňující odpověď příkazu

Uvidíte, jak zařízení reaguje na příkazy a aktualizace vlastností:

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

Procházet kód

Požadavky

K dokončení kroků v tomto článku potřebujete následující zdroje informací:

Kontrola kódu

V kopii úložiště Microsoft Azure IoT SDK pro jazyk C#, které jste si stáhli dříve, otevřete soubor řešení azure-iot-sdk-csharp-main\azureiot.sln v sadě Visual Studio. V Průzkumník řešení rozbalte složku PnpDeviceSamples > TemperatureController a otevřete Program.cs a TemperatureControllerSample.cs soubory, abyste zobrazili kód pro tuto ukázku.

Ukázka implementuje model více komponent Temperature Controller Digital Twin Definition Language.

Když spustíte ukázku pro připojení k IoT Central, zaregistruje zařízení pomocí služby Device Provisioning Service (DPS) a vygeneruje připojovací řetězec. Ukázka načte informace o připojení DPS, které potřebuje, z prostředí.

V Program.csMain metoda voláSetupDeviceClientAsync:

  • ID modelu dtmi:com:example:TemperatureController;2 použijte, když zřídí zařízení s DPS. IoT Central používá ID modelu k identifikaci nebo vygenerování šablony zařízení pro toto zařízení. Další informace najdete v tématu Přiřazení zařízení k šabloně zařízení.
  • Vytvořte instanci DeviceClient pro připojení k 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;
}

Hlavní metoda pak vytvoří TemperatureControllerSample instance a zavolá metodu PerformOperationsAsync pro zpracování interakcí s IoT Central.

V TemperatureControllerSample.csPerformOperationsAsync metoda:

  • Nastaví obslužnou rutinu pro příkaz reboot ve výchozí komponentě.
  • Nastaví obslužné rutiny pro příkazy getMaxMinReport na dvou komponentách termostatu.
  • Nastaví obslužné rutiny pro příjem aktualizací vlastností cílové teploty na dvou komponentách termostatu.
  • Odesílá počáteční aktualizace vlastností informací o zařízení.
  • Pravidelně odesílá telemetrii teploty ze dvou komponent termostatu.
  • Pravidelně odesílá telemetrii pracovní sady z výchozí komponenty.
  • Odešle maximální teplotu od posledního restartování při každém dosažení nové maximální teploty ve dvou komponentách termostatu.
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);
  }
}

Tato SendTemperatureAsync metoda ukazuje, jak zařízení odesílá telemetrii teploty ze komponenty do IoT Central. Metoda SendTemperatureTelemetryAsync používá PnpConvention třídu k sestavení zprávy:

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

Metoda UpdateMaxTemperatureSinceLastRebootAsync odešle maxTempSinceLastReboot aktualizaci vlastnosti do IoT Central. Tato metoda používá PnpConvention třídu k vytvoření opravy:

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

Metoda TargetTemperatureUpdateCallbackAsync zpracovává zapisovatelnou aktualizaci vlastnosti cílové teploty z IoT Central. Tato metoda používá PnpConvention třídu ke čtení zprávy aktualizace vlastnosti a sestavení odpovědi:

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

Metoda HandleMaxMinReportCommand zpracovává příkazy pro komponenty volané z 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)
    {
        // ...
    }
}

Získání informací o připojení

Když později v tomto kurzu spustíte ukázkovou aplikaci zařízení, budete potřebovat následující konfigurační hodnoty:

  • Rozsah ID: V aplikaci IoT Central přejděte do skupin připojení zařízení oprávnění>. Poznamenejte si hodnotu oboru ID.
  • Primární klíč skupiny: V aplikaci IoT Central přejděte na Skupiny > připojení zařízení OPRÁVNĚNÍ > SAS-IoT-Devices. Poznamenejte si hodnotu primárního klíče sdíleného přístupového podpisu.

Pomocí Azure Cloud Shellu vygenerujte klíč zařízení z primárního klíče skupiny, který jste získali:

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

Poznamenejte si vygenerovaný klíč zařízení, který použijete později v tomto kurzu.

Poznámka:

Pokud chcete tuto ukázku spustit, nemusíte zařízení předem zaregistrovat ve své aplikaci IoT Central. Ukázka používá funkci IoT Central k automatické registraci zařízení při prvním připojení.

Spuštění kódu

Poznámka:

Před spuštěním kódu nastavte TemperatureController jako spouštěný projekt.

Spuštění ukázkové aplikace v sadě Visual Studio:

  1. V Průzkumník řešení vyberte soubor projektu PnpDeviceSamples > TemperatureController.

  2. Přejděte do programu Project > TemperatureController – ladění vlastností>. Pak do projektu přidejte následující proměnné prostředí:

    Jméno Hodnota
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Hodnota oboru ID, kterou jste si poznamenali dříve.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY Vygenerovaná hodnota klíče zařízení, kterou jste si předtím poznamenali.

Ukázku teď můžete spustit a ladit v sadě Visual Studio.

Následující výstup ukazuje registraci zařízení a připojení k IoT Central. Ukázka začne odesílat telemetrii:

[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.

Jako operátor ve vaší aplikaci Azure IoT Central můžete:

  • Zobrazte telemetrii odesílanou dvěma komponentami termostatu na stránce Přehled :

    Snímek obrazovky znázorňující stránku s přehledem zařízení

  • Zobrazte vlastnosti zařízení na stránce O produktu. Tato stránka zobrazuje vlastnosti ze komponenty informací o zařízení a dvě komponenty termostatu:

    Snímek obrazovky znázorňující zobrazení vlastností zařízení

Přizpůsobení šablony zařízení

Jako vývojář řešení můžete přizpůsobit šablonu zařízení, kterou Služba IoT Central vytvořila automaticky při připojení zařízení kontroleru teploty.

Přidání cloudové vlastnosti pro uložení jména zákazníka přidruženého k zařízení:

  1. V aplikaci IoT Central přejděte na šablonu zařízení Teplotní kontroler na stránce Šablony zařízení.

  2. V modelu Teplotní kontroler vyberte +Přidat schopnost.

  3. Jako zobrazovaný název zadejte jméno zákazníka, jako typ schopnosti vyberte vlastnost Cloud, rozbalte položku a zvolte Řetězec jako schéma. Pak vyberte Uložit.

Přizpůsobení zobrazení příkazů sestavy Get Max-Min v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. V případě getMaxMinReport (termostat1) nahraďte sestavu Get Max-Min. Získáte termostat1 stavovou zprávu.

  3. V případě getMaxMinReport (termostat2) nahraďte sestavu Get Max-Min. Získáte zprávu o stavu termostat2.

  4. Zvolte Uložit.

Přizpůsobení způsobu zobrazení zapisovatelných vlastností cílové teploty v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. Pro targetTemperature (termostat1) nahraďte cílovou teplotu cílovou teplotou (1).

  3. Pro targetTemperature (termostat2) nahraďte cílovou teplotu cílovou teplotou (2).

  4. Zvolte Uložit.

Součástí termostatu v modelu kontroleru teploty je vlastnost Zapisovatelná cílová teplota , šablona zařízení obsahuje cloudovou vlastnost Customer Name . Vytvořte zobrazení, které může operátor použít k úpravě těchto vlastností:

  1. Vyberte Zobrazení a pak vyberte dlaždici Pro úpravy zařízení a cloudových dat .

  2. Jako název formuláře zadejte Vlastnosti .

  3. Vyberte vlastnosti Cílová teplota (1), Cílová teplota (2) a Jméno zákazníka. Pak vyberte Přidat oddíl.

  4. Uložte provedené změny.

Snímek obrazovky znázorňující zobrazení pro aktualizaci hodnot vlastností

Publikování šablony zařízení

Než operátor uvidí a použije vlastní nastavení, které jste provedli, musíte šablonu zařízení publikovat.

V šabloně termostatu vyberte Publikovat. Na panelu aplikace vyberte Publikovat tuto šablonu zařízení.

Operátor teď může pomocí zobrazení Vlastnosti aktualizovat hodnoty vlastností a volat příkazy s názvem Get termostat1 status report and Get termostat2 status report on the device commands page:

  • Aktualizujte zapisovatelné hodnoty vlastností na stránce Vlastnosti :

    Snímek obrazovky znázorňující aktualizaci vlastností zařízení

  • Volání příkazů ze stránky Příkazy Pokud spustíte příkaz zprávy o stavu, vyberte před spuštěním parametru Since datum a čas:

    Snímek obrazovky znázorňující volání příkazu

    Snímek obrazovky znázorňující odpověď příkazu

Uvidíte, jak zařízení reaguje na příkazy a aktualizace vlastností:

[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.

Procházet kód

Požadavky

K dokončení kroků v tomto článku potřebujete následující zdroje informací:

Kontrola kódu

V kopii sady Microsoft Azure IoT SDK pro Javu, kterou jste si stáhli dříve, otevřete 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 soubor v textovém editoru.

Ukázka implementuje model více komponent Temperature Controller Digital Twin Definition Language.

Když spustíte ukázku pro připojení k IoT Central, zaregistruje zařízení pomocí služby Device Provisioning Service (DPS) a vygeneruje připojovací řetězec. Ukázka načte informace o připojení DPS, které potřebuje, z prostředí příkazového řádku.

Metoda main:

  • Volání initializeAndProvisionDevice k nastavení dtmi:com:example:TemperatureController;2 ID modelu, zřízení a registrace zařízení pomocí DPS, vytvoření instance DeviceClient a připojení k aplikaci IoT Central. IoT Central používá ID modelu k identifikaci nebo vygenerování šablony zařízení pro toto zařízení. Další informace najdete v tématu Přiřazení zařízení k šabloně zařízení.
  • Vytvoří obslužné rutiny příkazů pro příkazy getMaxMinReport a reboot příkazy.
  • Vytvoří obslužné rutiny aktualizace vlastností pro zapisovatelné targetTemperature vlastnosti.
  • Odešle počáteční hodnoty vlastností v rozhraní Informace o zařízení a vlastnosti Paměť zařízení a Sériové číslo .
  • Spustí vlákno, které odešle telemetrii teploty ze dvou termostatů a aktualizuje maxTempSinceLastReboot vlastnost každých pět sekund.
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();
}

Tato initializeAndProvisionDevice metoda ukazuje, jak zařízení používá DPS k registraci a připojení k IoT Central. Datová část obsahuje ID modelu, které IoT Central používá k přiřazení zařízení k šabloně zařízení:

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

Tato sendTemperatureTelemetry metoda ukazuje, jak zařízení odesílá telemetrii teploty ze komponenty do IoT Central. Tato metoda používá PnpConvention třídu k vytvoření zprávy:

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

Metoda updateMaxTemperatureSinceLastReboot odešle maxTempSinceLastReboot aktualizaci vlastnosti ze komponenty do IoT Central. Tato metoda používá PnpConvention třídu k vytvoření opravy:

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

Třída TargetTemperatureUpdateCallback obsahuje metodu onPropertyChanged pro zpracování zapisovatelných vlastností aktualizace komponenty z IoT Central. Tato metoda používá PnpConvention třídu k vytvoření odpovědi:

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

Třída MethodCallback obsahuje metodu onMethodInvoked pro zpracování příkazů komponent volané z 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);
        }
    }
}

Získání informací o připojení

Když později v tomto kurzu spustíte ukázkovou aplikaci zařízení, budete potřebovat následující konfigurační hodnoty:

  • Rozsah ID: V aplikaci IoT Central přejděte do skupin připojení zařízení oprávnění>. Poznamenejte si hodnotu oboru ID.
  • Primární klíč skupiny: V aplikaci IoT Central přejděte na Skupiny > připojení zařízení OPRÁVNĚNÍ > SAS-IoT-Devices. Poznamenejte si hodnotu primárního klíče sdíleného přístupového podpisu.

Pomocí Azure Cloud Shellu vygenerujte klíč zařízení z primárního klíče skupiny, který jste získali:

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

Poznamenejte si vygenerovaný klíč zařízení, který použijete později v tomto kurzu.

Poznámka:

Pokud chcete tuto ukázku spustit, nemusíte zařízení předem zaregistrovat ve své aplikaci IoT Central. Ukázka používá funkci IoT Central k automatické registraci zařízení při prvním připojení.

Ve Windows přejděte do kořenové složky úložiště Azure IoT SDK pro Javu, které jste stáhli.

Spuštěním následujícího příkazu sestavte ukázkovou aplikaci:

mvn install -T 2C -DskipTests

Spuštění kódu

Pokud chcete spustit ukázkovou aplikaci, otevřete prostředí příkazového řádku a přejděte do složky azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample , která obsahuje složku src s ukázkovým souborem TemperatureController.java .

Nastavte proměnné prostředí pro konfiguraci ukázky. Následující fragment kódu ukazuje, jak nastavit proměnné prostředí na příkazovém řádku Windows. Pokud používáte prostředí Bash , nahraďte set příkazy export příkazy:

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

Spusťte ukázku:

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

Následující výstup ukazuje registraci zařízení a připojení k IoT Central. Ukázka začne odesílat telemetrii:

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 [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] 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 [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] 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 [cccc2222-dd33-4444-55ee-666666ffffff] Message Id [dddd3333-ee44-5555-66ff-777777aaaaaa] 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 [eeee4444-ff55-6666-77aa-888888bbbbbb] Message Id [ffff5555-aa66-7777-88bb-999999cccccc] 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 [aaaa6666-bb77-8888-99cc-000000dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] 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 [cccc2222-dd33-4444-55ee-666666ffffff] Message Id [dddd3333-ee44-5555-66ff-777777aaaaaa] 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 [bbbb1111-cc22-3333-44dd-555555eeeeee] Message Id [cccc2222-dd33-4444-55ee-666666ffffff] 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 [dddd3333-ee44-5555-66ff-777777aaaaaa] Message Id [eeee4444-ff55-6666-77aa-888888bbbbbb] )
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 [ffff5555-aa66-7777-88bb-999999cccccc] Message Id [aaaa6666-bb77-8888-99cc-000000dddddd] 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 [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id bbbb1111-cc22-3333-44dd-555555eeeeee.

Jako operátor ve vaší aplikaci Azure IoT Central můžete:

  • Zobrazte telemetrii odesílanou dvěma komponentami termostatu na stránce Přehled :

    Snímek obrazovky znázorňující stránku s přehledem zařízení

  • Zobrazte vlastnosti zařízení na stránce O produktu. Tato stránka zobrazuje vlastnosti ze komponenty informací o zařízení a dvě komponenty termostatu:

    Snímek obrazovky znázorňující zobrazení vlastností zařízení

Přizpůsobení šablony zařízení

Jako vývojář řešení můžete přizpůsobit šablonu zařízení, kterou Služba IoT Central vytvořila automaticky při připojení zařízení kontroleru teploty.

Přidání cloudové vlastnosti pro uložení jména zákazníka přidruženého k zařízení:

  1. V aplikaci IoT Central přejděte na šablonu zařízení Teplotní kontroler na stránce Šablony zařízení.

  2. V modelu Teplotní kontroler vyberte +Přidat schopnost.

  3. Jako zobrazovaný název zadejte jméno zákazníka, jako typ schopnosti vyberte vlastnost Cloud, rozbalte položku a zvolte Řetězec jako schéma. Pak vyberte Uložit.

Přizpůsobení zobrazení příkazů sestavy Get Max-Min v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. V případě getMaxMinReport (termostat1) nahraďte sestavu Get Max-Min. Získáte termostat1 stavovou zprávu.

  3. V případě getMaxMinReport (termostat2) nahraďte sestavu Get Max-Min. Získáte zprávu o stavu termostat2.

  4. Zvolte Uložit.

Přizpůsobení způsobu zobrazení zapisovatelných vlastností cílové teploty v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. Pro targetTemperature (termostat1) nahraďte cílovou teplotu cílovou teplotou (1).

  3. Pro targetTemperature (termostat2) nahraďte cílovou teplotu cílovou teplotou (2).

  4. Zvolte Uložit.

Součástí termostatu v modelu kontroleru teploty je vlastnost Zapisovatelná cílová teplota , šablona zařízení obsahuje cloudovou vlastnost Customer Name . Vytvořte zobrazení, které může operátor použít k úpravě těchto vlastností:

  1. Vyberte Zobrazení a pak vyberte dlaždici Pro úpravy zařízení a cloudových dat .

  2. Jako název formuláře zadejte Vlastnosti .

  3. Vyberte vlastnosti Cílová teplota (1), Cílová teplota (2) a Jméno zákazníka. Pak vyberte Přidat oddíl.

  4. Uložte provedené změny.

Snímek obrazovky znázorňující zobrazení pro aktualizaci hodnot vlastností

Publikování šablony zařízení

Než operátor uvidí a použije vlastní nastavení, které jste provedli, musíte šablonu zařízení publikovat.

V šabloně termostatu vyberte Publikovat. Na panelu aplikace vyberte Publikovat tuto šablonu zařízení.

Operátor teď může pomocí zobrazení Vlastnosti aktualizovat hodnoty vlastností a volat příkazy s názvem Get termostat1 status report and Get termostat2 status report on the device commands page:

  • Aktualizujte zapisovatelné hodnoty vlastností na stránce Vlastnosti :

    Snímek obrazovky znázorňující aktualizaci vlastností zařízení

  • Volání příkazů ze stránky Příkazy Pokud spustíte příkaz zprávy o stavu, vyberte před spuštěním parametru Since datum a čas:

    Snímek obrazovky znázorňující volání příkazu

    Snímek obrazovky znázorňující odpověď příkazu

Uvidíte, jak zařízení reaguje na příkazy a aktualizace vlastností:

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

Procházet kód

Požadavky

K dokončení kroků v tomto článku potřebujete následující zdroje informací:

  • Vývojový počítač s nainstalovaným Node.js verze 6 nebo novější. Verzi můžete zkontrolovat spuštěním node --version na příkazovém řádku. Pokyny v tomto kurzu předpokládají, že spouštíte příkaz node na příkazovém řádku Windows. Můžete ale použít Node.js v mnoha dalších operačních systémech.

  • Místní kopie sady Microsoft Azure IoT SDK pro Node.js úložiště GitHub, která obsahuje ukázkový kód. Pomocí tohoto odkazu si můžete stáhnout kopii úložiště: Stáhnout soubor ZIP. Potom soubor rozbalte do vhodného umístění na místním počítači.

Kontrola kódu

V kopii sady Microsoft Azure IoT SDK pro Node.js, kterou jste si stáhli dříve, otevřete soubor azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js v textovém editoru.

Ukázka implementuje model více komponent Temperature Controller Digital Twin Definition Language.

Když spustíte ukázku pro připojení k IoT Central, zaregistruje zařízení pomocí služby Device Provisioning Service (DPS) a vygeneruje připojovací řetězec. Ukázka načte informace o připojení DPS, které potřebuje, z prostředí příkazového řádku.

Metoda main:

  • client Vytvoří objekt a nastaví dtmi:com:example:TemperatureController;2 ID modelu před otevřením připojení. IoT Central používá ID modelu k identifikaci nebo vygenerování šablony zařízení pro toto zařízení. Další informace najdete v tématu Přiřazení zařízení k šabloně zařízení.
  • Vytvoří obslužné rutiny příkazů pro tři příkazy.
  • Spustí smyčku pro každou termostatovou komponentu, která každých 5 sekund odesílá telemetrii teploty.
  • Spustí smyčku pro výchozí komponentu pro odesílání telemetrie velikosti pracovní sady každých 6 sekund.
  • maxTempSinceLastReboot Odešle vlastnost pro každou termostatovou komponentu.
  • Odešle vlastnosti informací o zařízení.
  • Vytvoří obslužné rutiny zapisovatelných vlastností pro tři komponenty.
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());
  }
}

Funkce provisionDevice ukazuje, jak zařízení používá DPS k registraci a připojení k IoT Central. Datová část obsahuje ID modelu, které IoT Central používá k přiřazení zařízení k šabloně zařízení:

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

Funkce sendTelemetry ukazuje, jak zařízení odesílá telemetrii teploty do IoT Central. Pro telemetrii ze součástí přidá vlastnost volanou $.sub s názvem komponenty:

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

Metoda main používá pomocnou metodu volanou helperCreateReportedPropertiesPatch k vytvoření zpráv aktualizace vlastností. Tato metoda přebírá volitelný parametr k určení komponenty odesílající vlastnost:

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

Metoda main používá následující metodu ke zpracování aktualizací zapisovatelných vlastností z IoT Central. Všimněte si, jak metoda sestaví odpověď pomocí verze a stavového kódu:

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

Metoda main používá k zpracování příkazů z IoT Central následující metody:

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

Získání informací o připojení

Když později v tomto kurzu spustíte ukázkovou aplikaci zařízení, budete potřebovat následující konfigurační hodnoty:

  • Rozsah ID: V aplikaci IoT Central přejděte do skupin připojení zařízení oprávnění>. Poznamenejte si hodnotu oboru ID.
  • Primární klíč skupiny: V aplikaci IoT Central přejděte na Skupiny > připojení zařízení OPRÁVNĚNÍ > SAS-IoT-Devices. Poznamenejte si hodnotu primárního klíče sdíleného přístupového podpisu.

Pomocí Azure Cloud Shellu vygenerujte klíč zařízení z primárního klíče skupiny, který jste získali:

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

Poznamenejte si vygenerovaný klíč zařízení, který použijete později v tomto kurzu.

Poznámka:

Pokud chcete tuto ukázku spustit, nemusíte zařízení předem zaregistrovat ve své aplikaci IoT Central. Ukázka používá funkci IoT Central k automatické registraci zařízení při prvním připojení.

Spuštění kódu

Pokud chcete spustit ukázkovou aplikaci, otevřete prostředí příkazového řádku a přejděte do složky azure-iot-sdk-node/device/samples/javascriptová složka, která obsahuje ukázkový soubor pnp_temperature_controller.js .

Nastavte proměnné prostředí pro konfiguraci ukázky. Následující fragment kódu ukazuje, jak nastavit proměnné prostředí na příkazovém řádku Windows. Pokud používáte prostředí Bash , nahraďte set příkazy export příkazy:

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

Nainstalujte požadované balíčky:

npm install

Spusťte ukázku:

node pnp_temperature_controller.js

Následující výstup ukazuje registraci zařízení a připojení k IoT Central. Ukázka pak odešle maxTempSinceLastReboot vlastnost ze dvou termostatických komponent předtím, než začne odesílat telemetrii:

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

Jako operátor ve vaší aplikaci Azure IoT Central můžete:

  • Zobrazte telemetrii odesílanou dvěma komponentami termostatu na stránce Přehled :

    Snímek obrazovky znázorňující stránku s přehledem zařízení

  • Zobrazte vlastnosti zařízení na stránce O produktu. Tato stránka zobrazuje vlastnosti ze komponenty informací o zařízení a dvě komponenty termostatu:

    Snímek obrazovky znázorňující zobrazení vlastností zařízení

Přizpůsobení šablony zařízení

Jako vývojář řešení můžete přizpůsobit šablonu zařízení, kterou Služba IoT Central vytvořila automaticky při připojení zařízení kontroleru teploty.

Přidání cloudové vlastnosti pro uložení jména zákazníka přidruženého k zařízení:

  1. V aplikaci IoT Central přejděte na šablonu zařízení Teplotní kontroler na stránce Šablony zařízení.

  2. V modelu Teplotní kontroler vyberte +Přidat schopnost.

  3. Jako zobrazovaný název zadejte jméno zákazníka, jako typ schopnosti vyberte vlastnost Cloud, rozbalte položku a zvolte Řetězec jako schéma. Pak vyberte Uložit.

Přizpůsobení zobrazení příkazů sestavy Get Max-Min v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. V případě getMaxMinReport (termostat1) nahraďte sestavu Get Max-Min. Získáte termostat1 stavovou zprávu.

  3. V případě getMaxMinReport (termostat2) nahraďte sestavu Get Max-Min. Získáte zprávu o stavu termostat2.

  4. Zvolte Uložit.

Přizpůsobení způsobu zobrazení zapisovatelných vlastností cílové teploty v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. Pro targetTemperature (termostat1) nahraďte cílovou teplotu cílovou teplotou (1).

  3. Pro targetTemperature (termostat2) nahraďte cílovou teplotu cílovou teplotou (2).

  4. Zvolte Uložit.

Součástí termostatu v modelu kontroleru teploty je vlastnost Zapisovatelná cílová teplota , šablona zařízení obsahuje cloudovou vlastnost Customer Name . Vytvořte zobrazení, které může operátor použít k úpravě těchto vlastností:

  1. Vyberte Zobrazení a pak vyberte dlaždici Pro úpravy zařízení a cloudových dat .

  2. Jako název formuláře zadejte Vlastnosti .

  3. Vyberte vlastnosti Cílová teplota (1), Cílová teplota (2) a Jméno zákazníka. Pak vyberte Přidat oddíl.

  4. Uložte provedené změny.

Snímek obrazovky znázorňující zobrazení pro aktualizaci hodnot vlastností

Publikování šablony zařízení

Než operátor uvidí a použije vlastní nastavení, které jste provedli, musíte šablonu zařízení publikovat.

V šabloně termostatu vyberte Publikovat. Na panelu aplikace vyberte Publikovat tuto šablonu zařízení.

Operátor teď může pomocí zobrazení Vlastnosti aktualizovat hodnoty vlastností a volat příkazy s názvem Get termostat1 status report and Get termostat2 status report on the device commands page:

  • Aktualizujte zapisovatelné hodnoty vlastností na stránce Vlastnosti :

    Snímek obrazovky znázorňující aktualizaci vlastností zařízení

  • Volání příkazů ze stránky Příkazy Pokud spustíte příkaz zprávy o stavu, vyberte před spuštěním parametru Since datum a čas:

    Snímek obrazovky znázorňující volání příkazu

    Snímek obrazovky znázorňující odpověď příkazu

Uvidíte, jak zařízení reaguje na příkazy a aktualizace vlastností. Příkaz getMaxMinReport je v komponentě thermostat2 , reboot příkaz je ve výchozí komponentě. Zapisovatelná targetTemperature vlastnost byla nastavena pro komponentu thermostat2 :

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

Procházet kód

Požadavky

K dokončení kroků v tomto článku potřebujete následující zdroje informací:

Kontrola kódu

V kopii sady Microsoft Azure IoT SDK pro Python, kterou jste si stáhli dříve, otevřete soubor azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py v textovém editoru.

Ukázka implementuje model více komponent Temperature Controller Digital Twin Definition Language.

Když spustíte ukázku pro připojení k IoT Central, zaregistruje zařízení pomocí služby Device Provisioning Service (DPS) a vygeneruje připojovací řetězec. Ukázka načte informace o připojení DPS, které potřebuje, z prostředí příkazového řádku.

Funkce main :

  • Používá službu DPS ke zřízení zařízení. Informace o zřizování zahrnují ID modelu. IoT Central používá ID modelu k identifikaci nebo vygenerování šablony zařízení pro toto zařízení. Další informace najdete v tématu Přiřazení zařízení k šabloně zařízení.
  • Device_client Vytvoří objekt a nastaví dtmi:com:example:TemperatureController;2 ID modelu před otevřením připojení.
  • Odešle počáteční hodnoty vlastností do IoT Central. Používá pnp_helper k vytvoření oprav.
  • Vytvoří naslouchací procesy pro příkazy getMaxMinReport a reboot příkazy. Každá komponenta termostatu má svůj vlastní getMaxMinReport příkaz.
  • Vytvoří naslouchací proces vlastností pro naslouchání aktualizacím zapisovatelných vlastností.
  • Spustí smyčku pro odesílání telemetrie teploty ze dvou komponent termostatu a funkční nastavení telemetrie z výchozí komponenty každých 8 sekund.
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())

    # ...

Tato provision_device funkce pomocí DPS zřídí zařízení a zaregistruje ho ve službě IoT Central. Tato funkce zahrnuje ID modelu zařízení, které IoT Central používá k přiřazení zařízení k šabloně zařízení, v datové části zřizování:

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()

Funkce execute_command_listener zpracovává požadavky na příkazy, spustí max_min_handler funkci, když zařízení přijme getMaxMinReport příkaz pro součásti termostatu reboot_handler a funkci, když zařízení obdrží reboot příkaz. Pomocí pnp_helper modulu sestaví odpověď:

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

Zpracovává async def execute_property_listener aktualizace zapisovatelných vlastností, například targetTemperature pro komponenty termostatu, a generuje odpověď JSON. Pomocí pnp_helper modulu sestaví odpověď:

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)

Funkce send_telemetry_from_temp_controller odesílá telemetrické zprávy ze komponent termostatu do IoT Central. Modul používá pnp_helper k sestavení zpráv:

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)

Získání informací o připojení

Když později v tomto kurzu spustíte ukázkovou aplikaci zařízení, budete potřebovat následující konfigurační hodnoty:

  • Rozsah ID: V aplikaci IoT Central přejděte do skupin připojení zařízení oprávnění>. Poznamenejte si hodnotu oboru ID.
  • Primární klíč skupiny: V aplikaci IoT Central přejděte na Skupiny > připojení zařízení OPRÁVNĚNÍ > SAS-IoT-Devices. Poznamenejte si hodnotu primárního klíče sdíleného přístupového podpisu.

Pomocí Azure Cloud Shellu vygenerujte klíč zařízení z primárního klíče skupiny, který jste získali:

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

Poznamenejte si vygenerovaný klíč zařízení, který použijete později v tomto kurzu.

Poznámka:

Pokud chcete tuto ukázku spustit, nemusíte zařízení předem zaregistrovat ve své aplikaci IoT Central. Ukázka používá funkci IoT Central k automatické registraci zařízení při prvním připojení.

Spuštění kódu

Pokud chcete spustit ukázkovou aplikaci, otevřete prostředí příkazového řádku a přejděte do složky azure-iot-sdk-python-2/samples/pnp , která obsahuje ukázkový soubor temp_controller_with_thermostats.py .

Nastavte proměnné prostředí pro konfiguraci ukázky. Následující fragment kódu ukazuje, jak nastavit proměnné prostředí na příkazovém řádku Windows. Pokud používáte prostředí Bash , nahraďte set příkazy export příkazy:

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

Nainstalujte požadované balíčky:

pip install azure-iot-device

Spusťte ukázku:

python temp_controller_with_thermostats.py

Následující výstup ukazuje registraci zařízení a připojení k IoT Central. Ukázka odešle maxTempSinceLastReboot vlastnosti ze dvou komponent termostatu před tím, než začne odesílat telemetrii:

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}

Jako operátor ve vaší aplikaci Azure IoT Central můžete:

  • Zobrazte telemetrii odesílanou dvěma komponentami termostatu na stránce Přehled :

    Snímek obrazovky znázorňující stránku s přehledem zařízení

  • Zobrazte vlastnosti zařízení na stránce O produktu. Tato stránka zobrazuje vlastnosti ze komponenty informací o zařízení a dvě komponenty termostatu:

    Snímek obrazovky znázorňující zobrazení vlastností zařízení

Přizpůsobení šablony zařízení

Jako vývojář řešení můžete přizpůsobit šablonu zařízení, kterou Služba IoT Central vytvořila automaticky při připojení zařízení kontroleru teploty.

Přidání cloudové vlastnosti pro uložení jména zákazníka přidruženého k zařízení:

  1. V aplikaci IoT Central přejděte na šablonu zařízení Teplotní kontroler na stránce Šablony zařízení.

  2. V modelu Teplotní kontroler vyberte +Přidat schopnost.

  3. Jako zobrazovaný název zadejte jméno zákazníka, jako typ schopnosti vyberte vlastnost Cloud, rozbalte položku a zvolte Řetězec jako schéma. Pak vyberte Uložit.

Přizpůsobení zobrazení příkazů sestavy Get Max-Min v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. V případě getMaxMinReport (termostat1) nahraďte sestavu Get Max-Min. Získáte termostat1 stavovou zprávu.

  3. V případě getMaxMinReport (termostat2) nahraďte sestavu Get Max-Min. Získáte zprávu o stavu termostat2.

  4. Zvolte Uložit.

Přizpůsobení způsobu zobrazení zapisovatelných vlastností cílové teploty v aplikaci IoT Central:

  1. Na stránce Šablony zařízení přejděte na šablonu zařízení Temperature Controller.

  2. Pro targetTemperature (termostat1) nahraďte cílovou teplotu cílovou teplotou (1).

  3. Pro targetTemperature (termostat2) nahraďte cílovou teplotu cílovou teplotou (2).

  4. Zvolte Uložit.

Součástí termostatu v modelu kontroleru teploty je vlastnost Zapisovatelná cílová teplota , šablona zařízení obsahuje cloudovou vlastnost Customer Name . Vytvořte zobrazení, které může operátor použít k úpravě těchto vlastností:

  1. Vyberte Zobrazení a pak vyberte dlaždici Pro úpravy zařízení a cloudových dat .

  2. Jako název formuláře zadejte Vlastnosti .

  3. Vyberte vlastnosti Cílová teplota (1), Cílová teplota (2) a Jméno zákazníka. Pak vyberte Přidat oddíl.

  4. Uložte provedené změny.

Snímek obrazovky znázorňující zobrazení pro aktualizaci hodnot vlastností

Publikování šablony zařízení

Než operátor uvidí a použije vlastní nastavení, které jste provedli, musíte šablonu zařízení publikovat.

V šabloně termostatu vyberte Publikovat. Na panelu aplikace vyberte Publikovat tuto šablonu zařízení.

Operátor teď může pomocí zobrazení Vlastnosti aktualizovat hodnoty vlastností a volat příkazy s názvem Get termostat1 status report and Get termostat2 status report on the device commands page:

  • Aktualizujte zapisovatelné hodnoty vlastností na stránce Vlastnosti :

    Snímek obrazovky znázorňující aktualizaci vlastností zařízení

  • Volání příkazů ze stránky Příkazy Pokud spustíte příkaz zprávy o stavu, vyberte před spuštěním parametru Since datum a čas:

    Snímek obrazovky znázorňující volání příkazu

    Snímek obrazovky znázorňující odpověď příkazu

Uvidíte, jak zařízení reaguje na příkazy a aktualizace vlastností:

{'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"}

Zobrazení nezpracovaných dat

Pomocí zobrazení nezpracovaných dat můžete zkoumat nezpracovaná data, která vaše zařízení odesílá do IoT Central:

Snímek obrazovky znázorňující nezpracované zobrazení dat

V tomto zobrazení můžete vybrat sloupce, které chcete zobrazit, a nastavit časový rozsah pro zobrazení. Nevymodelovaný datový sloupec zobrazuje data zařízení, která neodpovídají žádné definici vlastností nebo telemetrie v šabloně zařízení.

Vyčištění prostředků

Pokud nemáte v úmyslu dokončit žádné další rychlé starty nebo kurzy IoT Central, můžete aplikaci IoT Central odstranit:

  1. V aplikaci IoT Central přejděte do správy aplikací>.
  2. Vyberte Odstranit a potvrďte akci.

Další kroky

Pokud chcete pokračovat v sadě kurzů IoT Central a získat další informace o vytváření řešení IoT Central, přečtěte si téma: