Udostępnij za pośrednictwem


Poradnik: Utwórz i połącz aplikację kliencką z aplikacją Azure IoT Central

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Instrukcja pokazuje, jak połączyć aplikację kliencką z aplikacją Azure IoT Central. Aplikacja symuluje działanie urządzenia sterującego temperaturą. Kiedy aplikacja łączy się z IoT Central, wysyła identyfikator modelu urządzenia kontrolera temperatury. IoT Central używa identyfikatora modelu do pobrania modelu urządzenia i utworzenia dla Ciebie szablonu urządzenia. Dodajesz widoki do szablonu urządzenia, aby umożliwić operatorowi interakcję z urządzeniem.

W tym samouczku dowiesz się, jak:

  • Utwórz i uruchom kod urządzenia, a następnie zobacz, jak łączy się z aplikacją usługi IoT Central.
  • Wyświetl symulowane dane telemetryczne wysłane z urządzenia.
  • Dodawanie widoków niestandardowych do szablonu urządzenia.
  • Opublikuj szablon urządzenia.
  • Użyj widoku do zarządzania właściwościami urządzenia.
  • Wywołaj polecenie, aby kontrolować urządzenie.

Przeglądaj kod

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Wymagania wstępne

Aby ukończyć kroki w tym samouczku, potrzebujesz:

Ten samouczek można uruchomić w systemie Linux lub Windows. Polecenia powłoki w tym samouczku są zgodne z konwencją systemu Linux dla separatorów ścieżek "/", jeśli korzystasz z systemu Windows, pamiętaj, aby zamienić te separatory na "\".

Wymagania wstępne różnią się w zależności od systemu operacyjnego.

Linuxa

Ten samouczek zakłada, że używasz systemu Ubuntu Linux. Kroki w tym samouczku zostały przetestowane przy użyciu Ubuntu 18.04.

Aby ukończyć ten tutorial dotyczący systemu Linux, zainstaluj następujące oprogramowanie w swoim lokalnym środowisku Linux.

Zainstaluj GCC, Git, cmake oraz wszystkie wymagane zależności, używając polecenia apt-get.

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

Sprawdź, czy wersja GCC cmake jest większa niż 2.8.12 , a wersja GCC jest większa niż 4.4.7.

cmake --version
gcc --version

Windows

Aby ukończyć ten samouczek na Windows, zainstaluj następujące oprogramowanie w swoim lokalnym środowisku Windows.

Pobierz kod

W tym samouczku przygotujesz środowisko deweloperskie, które możesz użyć do klonowania i budowania Azure IoT Hub Device C SDK.

Otwórz wiersz polecenia w wybranym katalogu. Wykonaj następujące polecenie, aby sklonować repozytorium GitHub zawierające zestawy SDK i biblioteki języka C Azure IoT do tej lokalizacji:

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

Oczekuj, że ta operacja zajmie kilka minut, aby się zakończyć.

Przeglądanie kodu

W kopii Microsoft Azure IoT SDK dla C, którą wcześniej pobrałeś, otwórz pliki azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c oraz azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c w edytorze tekstu.

Przykład implementuje wieloskładnikowy model języka definicji cyfrowej reprezentacji bliźniaczej kontrolera temperatury .

Kiedy uruchamiasz przykład, aby połączyć się z IoT Central, używa on usługi rejestracji urządzenia (DPS) do zarejestrowania urządzenia i wygenerowania ciągu połączenia. Przykład pobiera informacje o połączeniu DPS, których potrzebuje, z środowiska wiersza poleceń.

W pnp_temperature_controller.c, funkcja main najpierw wywołuje CreateDeviceClientAndAllocateComponents, aby:

  • Ustaw identyfikator modelu dtmi:com:example:Thermostat;1. IoT Central używa identyfikatora modelu do zidentyfikowania lub wygenerowania szablonu urządzenia dla tego urządzenia. Aby dowiedzieć się więcej, zobacz Przypisywanie urządzenia do szablonu urządzenia.
  • Użyj DPS do przygotowania i zarejestrowania urządzenia.
  • Utwórz uchwyt klienta urządzenia i połącz się z aplikacją IoT Central.
  • Tworzy procedurę obsługi dla poleceń w komponencie kontrolera temperatury.
  • Tworzy obsługę dla aktualizacji właściwości w komponencie kontrolera temperatury.
  • Tworzy dwa elementy termostatu.

Funkcja main następna:

  • Raportuje niektóre początkowe wartości właściwości dla wszystkich komponentów.
  • Rozpoczyna pętlę w celu wysyłania danych telemetrycznych ze wszystkich komponentów.

Następnie main funkcja uruchamia wątek w celu okresowego wysyłania danych telemetrycznych.

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

W pnp_thermostat_component.c funkcja PnP_ThermostatComponent_SendCurrentTemperature pokazuje, jak urządzenie przesyła telemetrię temperatury z komponentu 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);
}

W pnp_thermostat_component.c funkcja PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property wysyła aktualizację właściwości maxTempSinceLastReboot z komponentu 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);
    }
}

W pnp_thermostat_component.c, funkcja PnP_ThermostatComponent_ProcessPropertyUpdate obsługuje aktualizacje zapisywalnych właściwości 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);
            }
        }
    }
}

W pnp_thermostat_component.c funkcja PnP_ThermostatComponent_ProcessCommand obsługuje polecenia wywoływane 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;
    }
}

Budowanie kodu

Do skompilowania dołączonego przykładowego kodu należy użyć zestawu SDK urządzenia:

  1. Utwórz podkatalog cmake w folderze głównym zestawu SDK urządzenia i przejdź do tego folderu:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Uruchom następujące polecenia, aby zbudować SDK i przykłady:

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

Uzyskaj informacje o połączeniu

Po uruchomieniu przykładowej aplikacji urządzenia w dalszej części tego samouczka potrzebne są następujące wartości konfiguracji:

  • Zakres identyfikatorów: w aplikacji usługi IoT Central przejdź do pozycji Uprawnienia > Grupy połączeń urządzeń. Zanotuj wartość ID scope.
  • Grupowy klucz główny: W aplikacji IoT Central przejdź do Uprawnienia > Grupy połączeń urządzeń > SAS-IoT-Devices. Zanotuj wartość klucza podstawowego sygnatury dostępu współdzielonego.

Użyj Azure Cloud Shell, aby wygenerować klucz urządzenia na podstawie klucza głównego grupy, który pobrałeś.

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

Zanotuj wygenerowany klucz urządzenia, ponieważ użyjesz go później w tym samouczku.

Uwaga / Notatka

Aby uruchomić ten przykład, nie musisz rejestrować urządzenia z wyprzedzeniem w swojej aplikacji IoT Central. W przykładzie użyto funkcji usługi IoT Central do automatycznego rejestrowania urządzeń po nawiązaniu pierwszego połączenia.

Uruchom kod

Otwórz środowisko wiersza poleceń i przejdź do folderu azure-iot-sdk-c\cmake, aby uruchomić przykładową aplikację.

Ustaw zmienne środowiskowe, aby skonfigurować próbkę. Poniższy fragment pokazuje, jak ustawić zmienne środowiskowe w wierszu poleceń systemu Windows. Jeśli używasz powłoki bash, zamień polecenia set na polecenia export.

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

Aby uruchomić przykład:

# 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

Następujący wynik pokazuje rejestrację urządzenia i łączenie z IoT Central. Próbka zaczyna przesyłać telemetrię.

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 operator w swojej aplikacji Azure IoT Central, możesz:

  • Zobacz telemetrię wysyłaną przez dwa komponenty termostatu na stronie Przegląd.

    Zrzut ekranu, który pokazuje stronę przeglądu urządzenia.

  • Zobacz właściwości urządzenia na stronie Informacje. Na tej stronie przedstawiono właściwości komponentu informacji o urządzeniu i dwóch składników termostatu:

    Zrzut ekranu przedstawiający widok właściwości urządzenia.

Dostosuj szablon urządzenia

Jako twórca rozwiązań możesz dostosować szablon urządzenia, który IoT Central utworzył automatycznie po połączeniu się urządzenia sterującego temperaturą.

Aby dodać właściwość chmury do przechowywania nazwy klienta powiązanej z urządzeniem:

  1. W aplikacji IoT Central przejdź do szablonu urządzenia Temperature Controller na stronie Szablony urządzeń.

  2. W modelu Regulatora Temperatury, wybierz +Dodaj funkcję.

  3. Wprowadź nazwę klienta jako nazwę wyświetlaną, wybierz właściwość chmury jako rodzaj możliwości, rozwiń wpis i wybierz ciąg znaków jako schemat. Następnie wybierz opcję Zapisz.

Aby dostosować sposób wyświetlania poleceń Get Max-Min report w aplikacji IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. W przypadku getMaxMinReport (thermostat1) zastąp pozycję Pobierz raport Max-Min raportemo stanie Pobierz termostat1.

  3. W przypadku getMaxMinReport (thermostat2) zastąp pozycję Pobierz raport Max-Min raportemo stanie polecenia Pobierz termostat2.

  4. Wybierz Zapisz.

Aby dostosować sposób wyświetlania właściwości zapisywalnych temperatury docelowej w aplikacji usługi IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. Dla targetTemperature (termostat1), zamień Temperatura docelowa na Temperatura docelowa (1).

  3. W przypadku targetTemperature (thermostat2) zamień Target Temperature na Target Temperature (2).

  4. Wybierz Zapisz.

Komponenty termostatu w modelu Temperature Controller obejmują zapisywalne właściwości Target Temperature, a szablon urządzenia zawiera właściwość chmurową Customer Name. Utwórz widok, którego operator może użyć do edycji tych właściwości.

  1. Wybierz pozycję Widoki , a następnie wybierz kafelek Edytowanie urządzenia i danych w chmurze .

  2. Wprowadź Właściwości jako nazwę formularza.

  3. Wybierz właściwości Temperatura docelowa (1), Temperatura docelowa (2) oraz Nazwa klienta. Następnie wybierz Dodaj sekcję.

  4. Zapisz swoje zmiany.

Zrzut ekranu przedstawiający widok do aktualizowania wartości właściwości.

Opublikuj szablon urządzenia

Zanim operator będzie mógł zobaczyć i użyć dostosowań, które wprowadziłeś, musisz opublikować szablon urządzenia.

Z szablonu urządzenia Termostat wybierz opcję Publikuj. Na panelu Publikuj ten szablon urządzenia do aplikacji wybierz Publikuj.

Operator może teraz używać widoku Właściwości do aktualizacji wartości właściwości oraz wywołać polecenia Pobierz raport statusu termostatu1 i Pobierz raport statusu termostatu2 na stronie poleceń urządzenia.

  • Zaktualizuj wartości właściwości, które można zapisywać, na stronie Właściwości:

    Zrzut ekranu pokazujący aktualizację właściwości urządzenia.

  • Wywołaj polecenia ze strony Commands. Jeśli wykonasz polecenie raportu statusu, wybierz datę i godzinę dla parametru Since przed jego uruchomieniem.

    Zrzut ekranu pokazujący wywołanie polecenia.

    Zrzut ekranu pokazujący odpowiedź na polecenie.

Możesz zobaczyć, jak urządzenie reaguje na polecenia i aktualizacje właściwości.

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

Przeglądaj kod

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Wymagania wstępne

Aby ukończyć kroki opisane w tym artykule, potrzebujesz następujących zasobów:

Przeglądanie kodu

W kopii repozytorium Microsoft Azure IoT SDK dla C#, które pobrałeś wcześniej, otwórz plik rozwiązania azure-iot-sdk-csharp-main\azureiot.sln w Visual Studio. W Eksploratorze rozwiązań rozwiń folder PnpDeviceSamples > TemperatureController i otwórz pliki Program.cs oraz TemperatureControllerSample.cs, aby zobaczyć kod dla tego przykładu.

Przykład implementuje wieloskładnikowy model języka definicji cyfrowej reprezentacji bliźniaczej kontrolera temperatury .

Kiedy uruchamiasz przykład, aby połączyć się z IoT Central, używa on usługi rejestracji urządzenia (DPS) do zarejestrowania urządzenia i wygenerowania ciągu połączenia. Przykład pobiera potrzebne informacje o połączeniu DPS ze środowiska.

W Program.cs, metoda Main wywołuje SetupDeviceClientAsync aby:

  • Użyj identyfikatora modelu dtmi:com:example:TemperatureController;2, gdy urządzenie zostanie obsłużone przez DPS. IoT Central używa identyfikatora modelu do zidentyfikowania lub wygenerowania szablonu urządzenia dla tego urządzenia. Aby dowiedzieć się więcej, zobacz Przypisywanie urządzenia do szablonu urządzenia.
  • Utwórz instancję DeviceClient, aby połączyć się z 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;
}

Metoda główna tworzy następnie instancję TemperatureControllerSample i wywołuje metodę PerformOperationsAsync, aby obsługiwać interakcje z IoT Central.

W TemperatureControllerSample.cs metoda PerformOperationsAsync :

  • Ustawia obsługę dla polecenia reboot na domyślnym komponencie.
  • Ustawia obsługujące dla poleceń getMaxMinReport na dwóch komponentach termostatu.
  • Ustawia obsługi, aby odbierać aktualizacje docelowej temperatury w dwóch komponentach termostatu.
  • Wysyła początkowe aktualizacje właściwości informacji o urządzeniu.
  • Okresowo przesyła dane telemetryczne dotyczące temperatury z dwóch komponentów termostatu.
  • Okresowo wysyła telemetrię zestawu roboczego z domyślnego komponentu.
  • Przesyła maksymalną temperaturę od ostatniego restartu, gdy w dwóch komponentach termostatu zostanie osiągnięta nowa maksymalna temperatura.
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);
  }
}

Metoda SendTemperatureAsync pokazuje, jak urządzenie przesyła dane telemetryczne temperatury z komponentu do IoT Central. Metoda SendTemperatureTelemetryAsync używa klasy PnpConvention do zbudowania wiadomości.

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 wysyła aktualizację właściwości maxTempSinceLastReboot do IoT Central. Ta metoda używa klasy PnpConvention do stworzenia poprawki:

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 obsługuje aktualizację zapisywalnej właściwości docelowej temperatury z IoT Central. Metoda ta wykorzystuje klasę PnpConvention do odczytu komunikatu aktualizacji właściwości i skonstruowania odpowiedzi.

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 obsługuje polecenia dla komponentów wywoływanych 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)
    {
        // ...
    }
}

Uzyskaj informacje o połączeniu

Po uruchomieniu przykładowej aplikacji urządzenia w dalszej części tego samouczka potrzebne są następujące wartości konfiguracji:

  • Zakres identyfikatorów: w aplikacji usługi IoT Central przejdź do pozycji Uprawnienia > Grupy połączeń urządzeń. Zanotuj wartość ID scope.
  • Grupowy klucz główny: W aplikacji IoT Central przejdź do Uprawnienia > Grupy połączeń urządzeń > SAS-IoT-Devices. Zanotuj wartość klucza podstawowego sygnatury dostępu współdzielonego.

Użyj Azure Cloud Shell, aby wygenerować klucz urządzenia na podstawie klucza głównego grupy, który pobrałeś.

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

Zanotuj wygenerowany klucz urządzenia, ponieważ użyjesz go później w tym samouczku.

Uwaga / Notatka

Aby uruchomić ten przykład, nie musisz rejestrować urządzenia z wyprzedzeniem w swojej aplikacji IoT Central. W przykładzie użyto funkcji usługi IoT Central do automatycznego rejestrowania urządzeń po nawiązaniu pierwszego połączenia.

Uruchom kod

Uwaga / Notatka

Skonfiguruj TemperatureController jako projekt startowy przed uruchomieniem kodu.

Aby uruchomić przykładową aplikację w Visual Studio:

  1. W Solution Explorer wybierz plik projektu PnpDeviceSamples > TemperatureController.

  2. Przejdź do debugowania właściwości > projektu > TemperatureController. Następnie dodaj następujące zmienne środowiskowe do projektu:

    Nazwa Wartość
    IOTHUB_DEVICE_SECURITY_TYPE Dom Pomocy Społecznej
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Wartość zakresu ID, którą wcześniej zanotowałeś.
    IOTHUB_DEVICE_DPS_DEVICE_ID przykładowe-urzadzenie-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY Wygenerowaną wartość klucza urządzenia, którą wcześniej zanotowałeś.

Możesz teraz uruchomić i debugować przykład w Visual Studio.

Następujący wynik pokazuje rejestrację urządzenia i łączenie z IoT Central. Próbka zaczyna przesyłać telemetrię.

[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 operator w swojej aplikacji Azure IoT Central, możesz:

  • Zobacz telemetrię wysyłaną przez dwa komponenty termostatu na stronie Przegląd.

    Zrzut ekranu, który pokazuje stronę przeglądu urządzenia.

  • Zobacz właściwości urządzenia na stronie Informacje. Na tej stronie przedstawiono właściwości komponentu informacji o urządzeniu i dwóch składników termostatu:

    Zrzut ekranu przedstawiający widok właściwości urządzenia.

Dostosuj szablon urządzenia

Jako twórca rozwiązań możesz dostosować szablon urządzenia, który IoT Central utworzył automatycznie po połączeniu się urządzenia sterującego temperaturą.

Aby dodać właściwość chmury do przechowywania nazwy klienta powiązanej z urządzeniem:

  1. W aplikacji IoT Central przejdź do szablonu urządzenia Temperature Controller na stronie Szablony urządzeń.

  2. W modelu Regulatora Temperatury, wybierz +Dodaj funkcję.

  3. Wprowadź nazwę klienta jako nazwę wyświetlaną, wybierz właściwość chmury jako rodzaj możliwości, rozwiń wpis i wybierz ciąg znaków jako schemat. Następnie wybierz opcję Zapisz.

Aby dostosować sposób wyświetlania poleceń Get Max-Min report w aplikacji IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. W przypadku getMaxMinReport (thermostat1) zastąp pozycję Pobierz raport Max-Min raportemo stanie Pobierz termostat1.

  3. W przypadku getMaxMinReport (thermostat2) zastąp pozycję Pobierz raport Max-Min raportemo stanie polecenia Pobierz termostat2.

  4. Wybierz Zapisz.

Aby dostosować sposób wyświetlania właściwości zapisywalnych temperatury docelowej w aplikacji usługi IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. Dla targetTemperature (termostat1), zamień Temperatura docelowa na Temperatura docelowa (1).

  3. W przypadku targetTemperature (thermostat2) zamień Target Temperature na Target Temperature (2).

  4. Wybierz Zapisz.

Komponenty termostatu w modelu Temperature Controller obejmują zapisywalne właściwości Target Temperature, a szablon urządzenia zawiera właściwość chmurową Customer Name. Utwórz widok, którego operator może użyć do edycji tych właściwości.

  1. Wybierz pozycję Widoki , a następnie wybierz kafelek Edytowanie urządzenia i danych w chmurze .

  2. Wprowadź Właściwości jako nazwę formularza.

  3. Wybierz właściwości Temperatura docelowa (1), Temperatura docelowa (2) oraz Nazwa klienta. Następnie wybierz Dodaj sekcję.

  4. Zapisz swoje zmiany.

Zrzut ekranu przedstawiający widok do aktualizowania wartości właściwości.

Opublikuj szablon urządzenia

Zanim operator będzie mógł zobaczyć i użyć dostosowań, które wprowadziłeś, musisz opublikować szablon urządzenia.

Z szablonu urządzenia Termostat wybierz opcję Publikuj. Na panelu Publikuj ten szablon urządzenia do aplikacji wybierz Publikuj.

Operator może teraz używać widoku Właściwości do aktualizacji wartości właściwości oraz wywołać polecenia Pobierz raport statusu termostatu1 i Pobierz raport statusu termostatu2 na stronie poleceń urządzenia.

  • Zaktualizuj wartości właściwości, które można zapisywać, na stronie Właściwości:

    Zrzut ekranu pokazujący aktualizację właściwości urządzenia.

  • Wywołaj polecenia ze strony Commands. Jeśli wykonasz polecenie raportu statusu, wybierz datę i godzinę dla parametru Since przed jego uruchomieniem.

    Zrzut ekranu pokazujący wywołanie polecenia.

    Zrzut ekranu pokazujący odpowiedź na polecenie.

Możesz zobaczyć, jak urządzenie reaguje na polecenia i aktualizacje właściwości.

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

Przeglądaj kod

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Wymagania wstępne

Aby ukończyć kroki opisane w tym artykule, potrzebujesz następujących zasobów:

Przeglądanie kodu

W kopii Microsoft Azure IoT SDK for Java, którą wcześniej pobrałeś, otwórz w edytorze tekstu plik 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.

Przykład implementuje wieloskładnikowy model języka definicji cyfrowej reprezentacji bliźniaczej kontrolera temperatury .

Kiedy uruchamiasz przykład, aby połączyć się z IoT Central, używa on usługi rejestracji urządzenia (DPS) do zarejestrowania urządzenia i wygenerowania ciągu połączenia. Przykład pobiera informacje o połączeniu DPS, których potrzebuje, z środowiska wiersza poleceń.

Metoda main:

  • Wywołanie initializeAndProvisionDevice w celu ustawienia dtmi:com:example:TemperatureController;2 ID modelu, użycie DPS do przygotowania i zarejestrowania urządzenia, utworzenie instancji DeviceClient i połączenie się z aplikacją IoT Central. IoT Central używa identyfikatora modelu do zidentyfikowania lub wygenerowania szablonu urządzenia dla tego urządzenia. Aby dowiedzieć się więcej, zobacz Przypisywanie urządzenia do szablonu urządzenia.
  • Tworzy obsługiwacze poleceń dla poleceń getMaxMinReport i reboot.
  • Tworzy obsługiwacze aktualizacji dla zapisywalnych właściwości targetTemperature.
  • Wysyła początkowe wartości dla właściwości w interfejsie Informacji o Urządzeniu oraz właściwości Pamięć Urządzenia i Numer Seryjny.
  • Uruchamia wątek do wysyłania telemetrii temperatury z dwóch termostatów i aktualizowania własności maxTempSinceLastReboot co pięć 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();
}

Metoda initializeAndProvisionDevice pokazuje, jak urządzenie używa usługi DPS do rejestrowania i nawiązywania połączenia z usługą IoT Central. Ładunek zawiera identyfikator modelu, którego IoT Central używa do przypisania urządzenia do szablonu urządzenia:

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

Metoda sendTemperatureTelemetry pokazuje, jak urządzenie przesyła dane telemetryczne temperatury z komponentu do IoT Central. Ta metoda używa PnpConvention klasy do utworzenia komunikatu:

  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 wysyła maxTempSinceLastReboot aktualizację właściwości ze składnika do usługi IoT Central. Ta metoda używa klasy PnpConvention do stworzenia poprawki:

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

Klasa TargetTemperatureUpdateCallback zawiera metodę onPropertyChanged do obsługi aktualizacji zapisywalnych właściwości komponentu z IoT Central. Ta metoda używa klasy PnpConvention do utworzenia odpowiedzi.

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

Klasa MethodCallback zawiera metodę onMethodInvoked do obsługi poleceń komponentów wywoływanych 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);
        }
    }
}

Uzyskaj informacje o połączeniu

Po uruchomieniu przykładowej aplikacji urządzenia w dalszej części tego samouczka potrzebne są następujące wartości konfiguracji:

  • Zakres identyfikatorów: w aplikacji usługi IoT Central przejdź do pozycji Uprawnienia > Grupy połączeń urządzeń. Zanotuj wartość ID scope.
  • Grupowy klucz główny: W aplikacji IoT Central przejdź do Uprawnienia > Grupy połączeń urządzeń > SAS-IoT-Devices. Zanotuj wartość klucza podstawowego sygnatury dostępu współdzielonego.

Użyj Azure Cloud Shell, aby wygenerować klucz urządzenia na podstawie klucza głównego grupy, który pobrałeś.

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

Zanotuj wygenerowany klucz urządzenia, ponieważ użyjesz go później w tym samouczku.

Uwaga / Notatka

Aby uruchomić ten przykład, nie musisz rejestrować urządzenia z wyprzedzeniem w swojej aplikacji IoT Central. W przykładzie użyto funkcji usługi IoT Central do automatycznego rejestrowania urządzeń po nawiązaniu pierwszego połączenia.

W systemie Windows przejdź do folderu głównego pobranego repozytorium zestawu Azure IoT SDK dla języka Java.

Uruchom następujące polecenie, aby zbudować przykładową aplikację:

mvn install -T 2C -DskipTests

Uruchom kod

Aby uruchomić przykładową aplikację, otwórz środowisko wiersza poleceń i przejdź do folderu azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample, który zawiera folder src z przykładowym plikiem TemperatureController.java.

Ustaw zmienne środowiskowe, aby skonfigurować próbkę. Poniższy fragment pokazuje, jak ustawić zmienne środowiskowe w wierszu poleceń systemu Windows. Jeśli używasz powłoki bash, zamień polecenia set na polecenia export.

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

Uruchom przykład:

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

Następujący wynik pokazuje rejestrację urządzenia i łączenie z IoT Central. Próbka zaczyna przesyłać telemetrię.

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 operator w swojej aplikacji Azure IoT Central, możesz:

  • Zobacz telemetrię wysyłaną przez dwa komponenty termostatu na stronie Przegląd.

    Zrzut ekranu, który pokazuje stronę przeglądu urządzenia.

  • Zobacz właściwości urządzenia na stronie Informacje. Na tej stronie przedstawiono właściwości komponentu informacji o urządzeniu i dwóch składników termostatu:

    Zrzut ekranu przedstawiający widok właściwości urządzenia.

Dostosuj szablon urządzenia

Jako twórca rozwiązań możesz dostosować szablon urządzenia, który IoT Central utworzył automatycznie po połączeniu się urządzenia sterującego temperaturą.

Aby dodać właściwość chmury do przechowywania nazwy klienta powiązanej z urządzeniem:

  1. W aplikacji IoT Central przejdź do szablonu urządzenia Temperature Controller na stronie Szablony urządzeń.

  2. W modelu Regulatora Temperatury, wybierz +Dodaj funkcję.

  3. Wprowadź nazwę klienta jako nazwę wyświetlaną, wybierz właściwość chmury jako rodzaj możliwości, rozwiń wpis i wybierz ciąg znaków jako schemat. Następnie wybierz opcję Zapisz.

Aby dostosować sposób wyświetlania poleceń Get Max-Min report w aplikacji IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. W przypadku getMaxMinReport (thermostat1) zastąp pozycję Pobierz raport Max-Min raportemo stanie Pobierz termostat1.

  3. W przypadku getMaxMinReport (thermostat2) zastąp pozycję Pobierz raport Max-Min raportemo stanie polecenia Pobierz termostat2.

  4. Wybierz Zapisz.

Aby dostosować sposób wyświetlania właściwości zapisywalnych temperatury docelowej w aplikacji usługi IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. Dla targetTemperature (termostat1), zamień Temperatura docelowa na Temperatura docelowa (1).

  3. W przypadku targetTemperature (thermostat2) zamień Target Temperature na Target Temperature (2).

  4. Wybierz Zapisz.

Komponenty termostatu w modelu Temperature Controller obejmują zapisywalne właściwości Target Temperature, a szablon urządzenia zawiera właściwość chmurową Customer Name. Utwórz widok, którego operator może użyć do edycji tych właściwości.

  1. Wybierz pozycję Widoki , a następnie wybierz kafelek Edytowanie urządzenia i danych w chmurze .

  2. Wprowadź Właściwości jako nazwę formularza.

  3. Wybierz właściwości Temperatura docelowa (1), Temperatura docelowa (2) oraz Nazwa klienta. Następnie wybierz Dodaj sekcję.

  4. Zapisz swoje zmiany.

Zrzut ekranu przedstawiający widok do aktualizowania wartości właściwości.

Opublikuj szablon urządzenia

Zanim operator będzie mógł zobaczyć i użyć dostosowań, które wprowadziłeś, musisz opublikować szablon urządzenia.

Z szablonu urządzenia Termostat wybierz opcję Publikuj. Na panelu Publikuj ten szablon urządzenia do aplikacji wybierz Publikuj.

Operator może teraz używać widoku Właściwości do aktualizacji wartości właściwości oraz wywołać polecenia Pobierz raport statusu termostatu1 i Pobierz raport statusu termostatu2 na stronie poleceń urządzenia.

  • Zaktualizuj wartości właściwości, które można zapisywać, na stronie Właściwości:

    Zrzut ekranu pokazujący aktualizację właściwości urządzenia.

  • Wywołaj polecenia ze strony Commands. Jeśli wykonasz polecenie raportu statusu, wybierz datę i godzinę dla parametru Since przed jego uruchomieniem.

    Zrzut ekranu pokazujący wywołanie polecenia.

    Zrzut ekranu pokazujący odpowiedź na polecenie.

Możesz zobaczyć, jak urządzenie reaguje na polecenia i aktualizacje właściwości.

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

Przeglądaj kod

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Wymagania wstępne

Aby ukończyć kroki opisane w tym artykule, potrzebujesz następujących zasobów:

  • Maszyna deweloperska z zainstalowaną wersją Node.js 6 lub nowszą. Możesz uruchomić node --version w wierszu poleceń, aby sprawdzić swoją wersję. Instrukcje w tym poradniku zakładają, że uruchamiasz polecenie node w wierszu polecenia systemu Windows. Możesz jednak używać Node.js na wielu innych systemach operacyjnych.

  • Lokalna kopia Microsoft Azure IoT SDK for Node.js repository GitHub zawierający kod przykładowy. Użyj tego linku, aby pobrać kopię repozytorium: Pobierz ZIP. Następnie rozpakuj plik do odpowiedniej lokalizacji na swoim komputerze lokalnym.

Przeglądanie kodu

W kopii zestawu Microsoft Azure IoT SDK dla pobranych wcześniej Node.js otwórz plik pnp_temperature_controller.jsazure-iot-sdk-node/device/samples/javascript/ w edytorze tekstów.

Przykład implementuje wieloskładnikowy model języka definicji cyfrowej reprezentacji bliźniaczej kontrolera temperatury .

Kiedy uruchamiasz przykład, aby połączyć się z IoT Central, używa on usługi rejestracji urządzenia (DPS) do zarejestrowania urządzenia i wygenerowania ciągu połączenia. Przykład pobiera informacje o połączeniu DPS, których potrzebuje, z środowiska wiersza poleceń.

Metoda main:

  • Tworzy obiekt client i ustawia identyfikator modelu dtmi:com:example:TemperatureController;2 przed otwarciem połączenia. IoT Central używa identyfikatora modelu do zidentyfikowania lub wygenerowania szablonu urządzenia dla tego urządzenia. Aby dowiedzieć się więcej, zobacz Przypisywanie urządzenia do szablonu urządzenia.
  • Tworzy obsługiwacze poleceń dla trzech poleceń.
  • Uruchamia pętlę dla każdego składnika termostatu, aby wysyłać dane telemetryczne dotyczące temperatury co 5 sekund.
  • Uruchamia pętlę dla domyślnego składnika, aby wysyłać dane telemetryczne o rozmiarze zestawu roboczego co 6 sekund.
  • Wysyła właściwość maxTempSinceLastReboot dla każdego komponentu termostatu.
  • Wysyła właściwości informacji o urządzeniu.
  • Tworzy moduły obsługujące zapisywalne właściwości dla trzech komponentów.
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());
  }
}

Ta provisionDevice funkcja pokazuje, w jaki sposób urządzenie używa usługi DPS do rejestrowania i nawiązywania połączenia z usługą IoT Central. Ładunek zawiera identyfikator modelu, którego IoT Central używa do Przypisania urządzenia do szablonu urządzenia:

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

Funkcja sendTelemetry pokazuje, jak urządzenie wysyła telemetryczne dane dotyczące temperatury do IoT Central. W przypadku danych telemetrycznych ze składników dodaje właściwość wywoływaną $.sub z nazwą składnika:

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 używa metody pomocniczej helperCreateReportedPropertiesPatch do tworzenia wiadomości o aktualizacji właściwości. Ta metoda przyjmuje opcjonalny parametr do określenia komponentu wysyłającego właściwość.

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 używa następującej metody do obsługi aktualizacji zapisywalnych właściwości z IoT Central. Zwróć uwagę, jak metoda buduje odpowiedź, uwzględniając wersję i kod statusu:

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 używa następujących metod do obsługi poleceń z IoT Central:

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

Uzyskaj informacje o połączeniu

Po uruchomieniu przykładowej aplikacji urządzenia w dalszej części tego samouczka potrzebne są następujące wartości konfiguracji:

  • Zakres identyfikatorów: w aplikacji usługi IoT Central przejdź do pozycji Uprawnienia > Grupy połączeń urządzeń. Zanotuj wartość ID scope.
  • Grupowy klucz główny: W aplikacji IoT Central przejdź do Uprawnienia > Grupy połączeń urządzeń > SAS-IoT-Devices. Zanotuj wartość klucza podstawowego sygnatury dostępu współdzielonego.

Użyj Azure Cloud Shell, aby wygenerować klucz urządzenia na podstawie klucza głównego grupy, który pobrałeś.

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

Zanotuj wygenerowany klucz urządzenia, ponieważ użyjesz go później w tym samouczku.

Uwaga / Notatka

Aby uruchomić ten przykład, nie musisz rejestrować urządzenia z wyprzedzeniem w swojej aplikacji IoT Central. W przykładzie użyto funkcji usługi IoT Central do automatycznego rejestrowania urządzeń po nawiązaniu pierwszego połączenia.

Uruchom kod

Aby uruchomić przykładową aplikację, otwórz środowisko wiersza polecenia i przejdź do folderu azure-iot-sdk-node/device/samples/javascript zawierającego pnp_temperature_controller.js przykładowy plik.

Ustaw zmienne środowiskowe, aby skonfigurować próbkę. Poniższy fragment pokazuje, jak ustawić zmienne środowiskowe w wierszu poleceń systemu Windows. Jeśli używasz powłoki bash, zamień polecenia set na polecenia export.

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

Zainstaluj wymagane pakiety:

npm install

Uruchom przykład:

node pnp_temperature_controller.js

Następujący wynik pokazuje rejestrację urządzenia i łączenie z IoT Central. Przykład następnie wysyła właściwość maxTempSinceLastReboot z dwóch elementów termostatu, zanim zacznie wysyłać dane telemetryczne.

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 operator w swojej aplikacji Azure IoT Central, możesz:

  • Zobacz telemetrię wysyłaną przez dwa komponenty termostatu na stronie Przegląd.

    Zrzut ekranu, który pokazuje stronę przeglądu urządzenia.

  • Zobacz właściwości urządzenia na stronie Informacje. Na tej stronie przedstawiono właściwości komponentu informacji o urządzeniu i dwóch składników termostatu:

    Zrzut ekranu przedstawiający widok właściwości urządzenia.

Dostosuj szablon urządzenia

Jako twórca rozwiązań możesz dostosować szablon urządzenia, który IoT Central utworzył automatycznie po połączeniu się urządzenia sterującego temperaturą.

Aby dodać właściwość chmury do przechowywania nazwy klienta powiązanej z urządzeniem:

  1. W aplikacji IoT Central przejdź do szablonu urządzenia Temperature Controller na stronie Szablony urządzeń.

  2. W modelu Regulatora Temperatury, wybierz +Dodaj funkcję.

  3. Wprowadź nazwę klienta jako nazwę wyświetlaną, wybierz właściwość chmury jako rodzaj możliwości, rozwiń wpis i wybierz ciąg znaków jako schemat. Następnie wybierz opcję Zapisz.

Aby dostosować sposób wyświetlania poleceń Get Max-Min report w aplikacji IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. W przypadku getMaxMinReport (thermostat1) zastąp pozycję Pobierz raport Max-Min raportemo stanie Pobierz termostat1.

  3. W przypadku getMaxMinReport (thermostat2) zastąp pozycję Pobierz raport Max-Min raportemo stanie polecenia Pobierz termostat2.

  4. Wybierz Zapisz.

Aby dostosować sposób wyświetlania właściwości zapisywalnych temperatury docelowej w aplikacji usługi IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. Dla targetTemperature (termostat1), zamień Temperatura docelowa na Temperatura docelowa (1).

  3. W przypadku targetTemperature (thermostat2) zamień Target Temperature na Target Temperature (2).

  4. Wybierz Zapisz.

Komponenty termostatu w modelu Temperature Controller obejmują zapisywalne właściwości Target Temperature, a szablon urządzenia zawiera właściwość chmurową Customer Name. Utwórz widok, którego operator może użyć do edycji tych właściwości.

  1. Wybierz pozycję Widoki , a następnie wybierz kafelek Edytowanie urządzenia i danych w chmurze .

  2. Wprowadź Właściwości jako nazwę formularza.

  3. Wybierz właściwości Temperatura docelowa (1), Temperatura docelowa (2) oraz Nazwa klienta. Następnie wybierz Dodaj sekcję.

  4. Zapisz swoje zmiany.

Zrzut ekranu przedstawiający widok do aktualizowania wartości właściwości.

Opublikuj szablon urządzenia

Zanim operator będzie mógł zobaczyć i użyć dostosowań, które wprowadziłeś, musisz opublikować szablon urządzenia.

Z szablonu urządzenia Termostat wybierz opcję Publikuj. Na panelu Publikuj ten szablon urządzenia do aplikacji wybierz Publikuj.

Operator może teraz używać widoku Właściwości do aktualizacji wartości właściwości oraz wywołać polecenia Pobierz raport statusu termostatu1 i Pobierz raport statusu termostatu2 na stronie poleceń urządzenia.

  • Zaktualizuj wartości właściwości, które można zapisywać, na stronie Właściwości:

    Zrzut ekranu pokazujący aktualizację właściwości urządzenia.

  • Wywołaj polecenia ze strony Commands. Jeśli wykonasz polecenie raportu statusu, wybierz datę i godzinę dla parametru Since przed jego uruchomieniem.

    Zrzut ekranu pokazujący wywołanie polecenia.

    Zrzut ekranu pokazujący odpowiedź na polecenie.

Możesz zobaczyć, jak urządzenie reaguje na polecenia i aktualizacje właściwości. Polecenie getMaxMinReport znajduje się thermostat2 w komponencie, polecenie reboot znajduje się w komponencie domyślnym. Ustawiono modyfikowalną właściwość targetTemperature dla 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

Przeglądaj kod

Ważne

Ten artykuł zawiera kroki dotyczące łączenia urządzenia przy użyciu podpisu umożliwiającego współdzielony dostęp, znanego również jako uwierzytelnianie kluczem symetrycznym. Ta metoda uwierzytelniania jest wygodna do testów i oceny, ale uwierzytelnianie urządzenia za pomocą certyfikatów X.509 jest bardziej bezpiecznym podejściem. Aby dowiedzieć się więcej, zobacz Security best practices for IoT solutions Connection security (Najlepsze rozwiązania w zakresie zabezpieczeń rozwiązań > IoT Connection Security).

Wymagania wstępne

Aby ukończyć kroki opisane w tym artykule, potrzebujesz następujących zasobów:

  • Maszyna deweloperska z zainstalowanym Python. Zapoznaj się z zestawem SDK języka Python usługi Azure IoT, aby zapoznać się z bieżącymi wymaganiami dotyczącymi wersji języka Python. Możesz uruchomić python --version w wierszu polecenia, aby sprawdzić swoją wersję. Python jest dostępny dla szerokiej gamy systemów operacyjnych. Instrukcje w tym samouczku zakładają, że uruchamiasz polecenie python w wierszu poleceń systemu Windows.

  • Lokalna kopia repozytorium GitHub Microsoft Azure IoT SDK for Python, która zawiera przykładowy kod. Użyj tego linku, aby pobrać kopię repozytorium: Pobierz ZIP. Następnie rozpakuj plik do odpowiedniej lokalizacji na swoim komputerze lokalnym.

Przeglądanie kodu

W kopii Microsoft Azure IoT SDK dla Python, którą pobrałeś wcześniej, otwórz plik azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py w edytorze tekstu.

Przykład implementuje wieloskładnikowy model języka definicji cyfrowej reprezentacji bliźniaczej kontrolera temperatury .

Kiedy uruchamiasz przykład, aby połączyć się z IoT Central, używa on usługi rejestracji urządzenia (DPS) do zarejestrowania urządzenia i wygenerowania ciągu połączenia. Przykład pobiera informacje o połączeniu DPS, których potrzebuje, z środowiska wiersza poleceń.

Funkcja main:

  • Używa usługi DPS do konfiguracji urządzenia. Informacje o wdrożeniu zawierają identyfikator modelu. IoT Central używa identyfikatora modelu do zidentyfikowania lub wygenerowania szablonu urządzenia dla tego urządzenia. Aby dowiedzieć się więcej, zobacz Przypisywanie urządzenia do szablonu urządzenia.
  • Tworzy obiekt Device_client i ustawia identyfikator modelu dtmi:com:example:TemperatureController;2 przed otwarciem połączenia.
  • Wysyła początkowe wartości właściwości do IoT Central. Używa pnp_helper, aby tworzyć łatki.
  • Tworzy nasłuchujących dla poleceń getMaxMinReport i reboot. Każdy element termostatu ma swoją własną komendę getMaxMinReport.
  • Tworzy nasłuchiwacz właściwości, który będzie nasłuchiwał aktualizacji możliwych do zapisania właściwości.
  • Uruchamia pętlę w celu wysyłania danych telemetrycznych temperatury z dwóch składników termostatu i telemetrii zestawu roboczego ze składnika domyślnego co 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())

    # ...

Funkcja provision_device używa usługi DPS do aprowizacji urządzenia i zarejestrowania go w usłudze IoT Central. Funkcja zawiera identyfikator modelu urządzenia, którego IoT Central używa do przypisania urządzenia do szablonu urządzenia w ładunku inicjalizacyjnym.

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

Funkcja execute_command_listener obsługuje żądania poleceń, uruchamia funkcję max_min_handler po otrzymaniu przez urządzenie polecenia getMaxMinReport dla komponentów termostatu oraz funkcję reboot_handler po otrzymaniu przez urządzenie polecenia reboot. Używa modułu pnp_helper do budowania odpowiedzi:

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

async def execute_property_listener obsługuje aktualizacje właściwości do zapisu, takie jak targetTemperature dla komponentów termostatu i generuje odpowiedź JSON. Używa modułu pnp_helper do budowania odpowiedzi:

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)

Funkcja send_telemetry_from_temp_controller wysyła wiadomości telemetryczne z komponentów termostatu do IoT Central. Używa modułu pnp_helper, aby tworzyć wiadomości:

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)

Uzyskaj informacje o połączeniu

Po uruchomieniu przykładowej aplikacji urządzenia w dalszej części tego samouczka potrzebne są następujące wartości konfiguracji:

  • Zakres identyfikatorów: w aplikacji usługi IoT Central przejdź do pozycji Uprawnienia > Grupy połączeń urządzeń. Zanotuj wartość ID scope.
  • Grupowy klucz główny: W aplikacji IoT Central przejdź do Uprawnienia > Grupy połączeń urządzeń > SAS-IoT-Devices. Zanotuj wartość klucza podstawowego sygnatury dostępu współdzielonego.

Użyj Azure Cloud Shell, aby wygenerować klucz urządzenia na podstawie klucza głównego grupy, który pobrałeś.

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

Zanotuj wygenerowany klucz urządzenia, ponieważ użyjesz go później w tym samouczku.

Uwaga / Notatka

Aby uruchomić ten przykład, nie musisz rejestrować urządzenia z wyprzedzeniem w swojej aplikacji IoT Central. W przykładzie użyto funkcji usługi IoT Central do automatycznego rejestrowania urządzeń po nawiązaniu pierwszego połączenia.

Uruchom kod

Aby uruchomić przykładową aplikację, otwórz środowisko wiersza poleceń i przejdź do folderu azure-iot-sdk-python-2/samples/pnp, który zawiera plik przykładowy temp_controller_with_thermostats.py.

Ustaw zmienne środowiskowe, aby skonfigurować próbkę. Poniższy fragment pokazuje, jak ustawić zmienne środowiskowe w wierszu poleceń systemu Windows. Jeśli używasz powłoki bash, zamień polecenia set na polecenia export.

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

Zainstaluj wymagane pakiety:

pip install azure-iot-device

Uruchom przykład:

python temp_controller_with_thermostats.py

Następujący wynik pokazuje rejestrację urządzenia i łączenie z IoT Central. Przykład przesyła właściwości maxTempSinceLastReboot z dwóch komponentów termostatu, zanim zacznie przesyłać dane telemetryczne.

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 operator w swojej aplikacji Azure IoT Central, możesz:

  • Zobacz telemetrię wysyłaną przez dwa komponenty termostatu na stronie Przegląd.

    Zrzut ekranu, który pokazuje stronę przeglądu urządzenia.

  • Zobacz właściwości urządzenia na stronie Informacje. Na tej stronie przedstawiono właściwości komponentu informacji o urządzeniu i dwóch składników termostatu:

    Zrzut ekranu przedstawiający widok właściwości urządzenia.

Dostosuj szablon urządzenia

Jako twórca rozwiązań możesz dostosować szablon urządzenia, który IoT Central utworzył automatycznie po połączeniu się urządzenia sterującego temperaturą.

Aby dodać właściwość chmury do przechowywania nazwy klienta powiązanej z urządzeniem:

  1. W aplikacji IoT Central przejdź do szablonu urządzenia Temperature Controller na stronie Szablony urządzeń.

  2. W modelu Regulatora Temperatury, wybierz +Dodaj funkcję.

  3. Wprowadź nazwę klienta jako nazwę wyświetlaną, wybierz właściwość chmury jako rodzaj możliwości, rozwiń wpis i wybierz ciąg znaków jako schemat. Następnie wybierz opcję Zapisz.

Aby dostosować sposób wyświetlania poleceń Get Max-Min report w aplikacji IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. W przypadku getMaxMinReport (thermostat1) zastąp pozycję Pobierz raport Max-Min raportemo stanie Pobierz termostat1.

  3. W przypadku getMaxMinReport (thermostat2) zastąp pozycję Pobierz raport Max-Min raportemo stanie polecenia Pobierz termostat2.

  4. Wybierz Zapisz.

Aby dostosować sposób wyświetlania właściwości zapisywalnych temperatury docelowej w aplikacji usługi IoT Central:

  1. Przejdź do szablonu urządzenia kontrolera temperatury na stronie Szablony urządzeń .

  2. Dla targetTemperature (termostat1), zamień Temperatura docelowa na Temperatura docelowa (1).

  3. W przypadku targetTemperature (thermostat2) zamień Target Temperature na Target Temperature (2).

  4. Wybierz Zapisz.

Komponenty termostatu w modelu Temperature Controller obejmują zapisywalne właściwości Target Temperature, a szablon urządzenia zawiera właściwość chmurową Customer Name. Utwórz widok, którego operator może użyć do edycji tych właściwości.

  1. Wybierz pozycję Widoki , a następnie wybierz kafelek Edytowanie urządzenia i danych w chmurze .

  2. Wprowadź Właściwości jako nazwę formularza.

  3. Wybierz właściwości Temperatura docelowa (1), Temperatura docelowa (2) oraz Nazwa klienta. Następnie wybierz Dodaj sekcję.

  4. Zapisz swoje zmiany.

Zrzut ekranu przedstawiający widok do aktualizowania wartości właściwości.

Opublikuj szablon urządzenia

Zanim operator będzie mógł zobaczyć i użyć dostosowań, które wprowadziłeś, musisz opublikować szablon urządzenia.

Z szablonu urządzenia Termostat wybierz opcję Publikuj. Na panelu Publikuj ten szablon urządzenia do aplikacji wybierz Publikuj.

Operator może teraz używać widoku Właściwości do aktualizacji wartości właściwości oraz wywołać polecenia Pobierz raport statusu termostatu1 i Pobierz raport statusu termostatu2 na stronie poleceń urządzenia.

  • Zaktualizuj wartości właściwości, które można zapisywać, na stronie Właściwości:

    Zrzut ekranu pokazujący aktualizację właściwości urządzenia.

  • Wywołaj polecenia ze strony Commands. Jeśli wykonasz polecenie raportu statusu, wybierz datę i godzinę dla parametru Since przed jego uruchomieniem.

    Zrzut ekranu pokazujący wywołanie polecenia.

    Zrzut ekranu pokazujący odpowiedź na polecenie.

Możesz zobaczyć, jak urządzenie reaguje na polecenia i aktualizacje właściwości.

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

Wyświetlanie danych pierwotnych

Możesz użyć widoku Surowe dane, aby zbadać surowe dane, które Twoje urządzenie wysyła do IoT Central.

Zrzut ekranu pokazujący widok surowych danych.

Na tym widoku możesz wybrać kolumny do wyświetlenia i ustawić zakres czasu do przeglądania. Kolumna Nieopracowane dane pokazuje dane urządzenia, które nie pasują do żadnych definicji właściwości ani telemetrii w szablonie urządzenia.

Uprzątnij zasoby

Jeśli nie planujesz kończyć kolejnych szybkich startów lub samouczków IoT Central, możesz usunąć swoją aplikację IoT Central.

  1. W aplikacji usługi IoT Central przejdź do pozycji Zarządzanie aplikacjami>.
  2. Wybierz Usuń, a następnie potwierdź swoją akcję.

Następne kroki

Jeśli wolisz kontynuować zestaw samouczków IoT Central i dowiedzieć się więcej o budowaniu rozwiązania IoT Central, zapoznaj się z: