Tutorial: Membuat dan menghubungkan aplikasi klien ke aplikasi Azure IoT Central Anda

Tutorial ini menunjukkan cara menghubungkan aplikasi klien ke aplikasi Azure IoT Central Anda. Aplikasi ini menyimulasikan perilaku perangkat pengontrol suhu. Ketika aplikasi terhubung ke IoT Central, aplikasi mengirimkan ID model dari model perangkat pengontrol suhu. IoT Central menggunakan ID model untuk mengambil model perangkat dan membuat templat perangkat untuk Anda. Anda menambahkan tampilan ke templat perangkat untuk memungkinkan operator berinteraksi dengan perangkat.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Buat dan jalankan kode perangkat, dan lihat kode perangkat tersebut terhubung ke aplikasi IoT Central Anda.
  • Tampilkan simulasi telemetri yang dikirim dari perangkat.
  • Tambahkan tampilan kustom ke templat perangkat.
  • Menerbitkan templat perangkat.
  • Gunakan tampilan untuk mengelola properti perangkat.
  • Panggil perintah untuk mengontrol perangkat.

Browse code

Prasyarat

Untuk menyelesaikan langkah-langkah dalam tutorial ini, Anda perlu:

Anda dapat menjalankan tutorial ini di Linux atau Windows. Perintah shell dalam tutorial ini mengikuti konvensi Linux untuk pemisah jalur '/', jika Anda mengikuti di Windows pastikan untuk menukar pemisah ini dengan '\'.

Prasyarat berbeda menurut sistem operasi:

Linux

Tutorial ini mengasumsikan Anda menggunakan Ubuntu Linux. Langkah-langkah dalam tutorial ini diuji menggunakan Ubuntu 18.04.

Untuk menyelesaikan tutorial ini di Linux, instal perangkat lunak berikut di lingkungan Linux lokal Anda:

PasangGCC, Git, cmake, dan semua dependensi yang diperlukan menggunakan perintah apt-get:

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

Verifikasi versi cmake lebih besar dari 2.8.12 dan versi GCC lebih besar dari 4.4.7.

cmake --version
gcc --version

Jendela

Untuk menyelesaikan tutorial ini di Windows, instal perangkat lunak berikut di lingkungan Windows lokal Anda:

Unduh kode

Dalam tutorial ini, Anda menyiapkan lingkungan pengembangan yang dapat Anda gunakan untuk mengkloning dan membangun Azure IoT Hub Device C SDK.

Buka perintah di direktori pilihan Anda. Jalankan perintah berikut untuk mengkloning repositori GitHub Azure IoT C SDKs dan Libraries ke lokasi ini:

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

Diperlukan waktu beberapa menit untuk menyelesaikan operasi ini.

Mengulas kode

Dalam salinan Microsoft Azure IoT SDK untuk C yang Anda unduh sebelumnya, buka azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c dan azure-iot- sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c file dalam editor teks.

Sampel mengimplementasikan model Temperature Controller Digital Twin Definition Language multi-komponen.

Saat Anda menjalankan sampel untuk terhubung ke IoT Central, sampel tersebut menggunakan Device Provisioning Service (DPS) untuk mendaftarkan perangkat dan menghasilkan string koneksi. Sampel mengambil informasi koneksi DPS yang dibutuhkan dari lingkungan baris perintah.

Di pnp_temperature_controller.c, fungsi main pertama-tama memanggil CreateDeviceClientAndAllocateComponents untuk:

  • Setel dtmi:com:example:Thermostat;1 ID model. IoT Central menggunakan ID model untuk mengidentifikasi atau membuat template perangkat untuk perangkat ini. Untuk mempelajari selengkapnya, lihat Menetapkan perangkat ke templat perangkat.
  • Gunakan DPS untuk menyediakan dan mendaftarkan perangkat.
  • Buat pegangan klien perangkat, dan sambungkan ke aplikasi IoT Central Anda.
  • Membuat handler untuk perintah dalam komponen pengontrol suhu.
  • Membuat handler untuk pembaruan properti di komponen pengontrol suhu.
  • Membuat dua komponen termostat.

Fungsi main berikutnya:

  • Melaporkan beberapa nilai properti awal untuk semua komponen.
  • Memulai loop untuk mengirim telemetri dari semua komponen.

Fungsi main kemudian memulai utas untuk mengirim telemetri secara berkala.

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

Di pnp_thermostat_component.c, fungsi PnP_ThermostatComponent_SendCurrentTemperature menunjukkan cara perangkat mengirim telemetri suhu dari komponen ke 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);
}

Di pnp_thermostat_component.c, fungsi PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property mengirimkan maxTempSinceLastReboot pembaruan properti dari komponen ke 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);
    }
}

Di pnp_thermostat_component.c, fungsi PnP_ThermostatComponent_ProcessPropertyUpdate menangani pembaruan properti yang dapat ditulis dari 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);
            }
        }
    }
}

Di pnp_thermostat_component.c, fungsi PnP_ThermostatComponent_ProcessCommand menangani perintah yang dipanggil dari 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;
    }
}

Bangun kode

Anda menggunakan SDK perangkat untuk membuat kode contoh yang disertakan:

  1. Buat subdirektori cmake di folder akar SDK perangkat, dan navigasikan ke folder itu:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Jalankan perintah berikut untuk membuat SDK dan sampel:

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

Mendapatkan informasi koneksi

Ketika Anda menjalankan aplikasi perangkat sampel dalam tutorial ini nanti, Anda memerlukan nilai konfigurasi berikut:

  • Cakupan ID: Di aplikasi IoT Central Anda, navigasikan ke grup koneksi Perangkat Izin>. Catat nilai lingkup ID.
  • Kunci utama grup: Di aplikasi IoT Central Anda, navigasikan ke Grup > koneksi Perangkat Izin > SAS-IoT-Devices. Catat nilai Kunci primer tanda tangan akses bersama.

Gunakan Azure Cloud Shell untuk menghasilkan kunci perangkat dari kunci utama grup yang Anda ambil:

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

Catat kunci perangkat yang dihasilkan, Anda gunakan nanti dalam tutorial ini.

Catatan

Untuk menjalankan sampel ini, Anda tidak perlu mendaftarkan perangkat terlebih dahulu di aplikasi IoT Central Anda. Sampel menggunakan kemampuan IoT Central untuk mendaftarkan perangkat secara otomatis saat tersambung untuk pertama kalinya.

Menjalankan kode

Untuk menjalankan aplikasi contoh, buka lingkungan baris perintah dan navigasikan ke folder azure-iot-sdk-c\cmake.

Atur variabel lingkungan untuk mengonfigurasi sampel. Cuplikan berikut menunjukkan bagaimana mengatur variabel lingkungan pada perintah Windows. Jika Anda menggunakan shell bash, ganti perintah set dengan perintah 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

Untuk menjalankan sampel:

# 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

Output berikut menunjukkan perangkat mendaftar dan menghubungkan ke IoT Central. Sampel mulai mengirimkan 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

Sebagai operator di aplikasi Azure IoT Central, Anda dapat:

  • Melihat telemetri yang dikirim oleh dua komponen termostat pada halaman Gambaran Umum:

    Screenshot that shows the device overview page.

  • Lihat properti perangkat pada halaman Tentang. Halaman ini menampilkan properti dari komponen informasi perangkat dan dua komponen termostat:

    Screenshot that shows the device properties view.

Menyesuaikan templat perangkat

Sebagai pengembang solusi, Anda dapat menyesuaikan templat perangkat yang dibuat IoT Central secara otomatis ketika perangkat pengontrol suhu terhubung.

Untuk menambahkan properti cloud untuk menyimpan nama pelanggan yang terkait dengan perangkat:

  1. Di aplikasi IoT Central Anda, navigasikan ke templat perangkat Pengontrol Suhu pada halaman Templat perangkat.

  2. Dalam model Pengontrol Suhu, pilih +Tambahkan kemampuan.

  3. Masukkan Nama pelanggan sebagai Nama tampilan, pilih Properti cloud sebagai jenis kemampuan, perluas entri dan pilih String sebagai Skema. Kemudian pilih Simpan.

Untuk menyesuaikan cara perintah Mendapatkan laporan Max-Min ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk getMaxMinReport (termostat1), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat1.

  3. Untuk getMaxMinReport (termostat2), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat2.

  4. Pilih Simpan.

Untuk mengkustomisasi cara Suhu Target properti yang dapat ditulis ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk targetTemperature (thermostat1), ganti Suhu Target dengan Suhu Target (1).

  3. Untuk targetTemperature (thermostat2), ganti Suhu Target dengan Suhu Target (2).

  4. Pilih Simpan.

Komponen termostat dalam model Pengontrol Suhu termasuk properti yang dapat ditulis Suhu Target, templat perangkat termasuk properti cloud Nama Pelanggan. Membuat tampilan yang dapat digunakan operator untuk mengedit properti ini:

  1. Pilih Tampilan lalu pilih petak Pengeditan perangkat dan data cloud.

  2. Masukkan Properti sebagai nama formulir.

  3. Pilih properti Suhu Target (1), Suhu Target (2), dan Nama Pelanggan. Kemudian pilih Tambahkan bagian.

  4. Simpan perubahan.

Screenshot that shows a view for updating property values.

Menerbitkan templat perangkat

Sebelum operator dapat melihat dan menggunakan kustomisasi yang Anda buat, Anda harus menerbitkan templat perangkat.

Dari templat perangkat Termostat, pilih Terbitkan. Pada halaman Terbitkan template perangkat ini ke panel aplikasi, pilih Terbitkan.

Operator sekarang dapat menggunakan tampilan Properti untuk memperbarui nilai properti, dan perintah panggilan yang disebut Get thermostat1 status report dan Get thermostat2 status report di halaman perintah perangkat:

  • Perbarui nilai properti yang dapat ditulis pada halaman Properti:

    Screenshot that shows updating the device properties.

  • Panggil perintah dari halaman Perintah. Jika Anda menjalankan perintah laporan status, pilih tanggal dan waktu untuk parameter Sejak sebelum Anda menjalankannya:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Anda dapat melihat bagaimana perangkat merespons perintah dan pembaruan properti:

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

...

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

Browse code

Prasyarat

Untuk menyelesaikan langkah-langkah dalam artikel ini, Anda memerlukan sumber daya berikut:

Mengulas kode

Dalam salinan repositori Microsoft Azure IoT SDK for C# yang Anda unduh sebelumnya, buka file solusi azure-iot-sdk-csharp-main\azureiot.sln di Visual Studio. Di Penjelajah Solusi, perluas folder PnpDeviceSamples > TemperatureController dan buka file Program.cs dan TemperatureControllerSample.cs untuk melihat kode untuk sampel ini.

Sampel mengimplementasikan model Temperature Controller Digital Twin Definition Language multi-komponen.

Saat Anda menjalankan sampel untuk terhubung ke IoT Central, sampel tersebut menggunakan Device Provisioning Service (DPS) untuk mendaftarkan perangkat dan menghasilkan string koneksi. Sampel mengambil informasi koneksi DPS yang dibutuhkan dari lingkungan.

Pada Program.cs, metode Main memanggil SetupDeviceClientAsync untuk:

  • Gunakan ID model dtmi:com:example:TemperatureController;2 ​​saat memprovisikan perangkat dengan DPS. IoT Central menggunakan ID model untuk mengidentifikasi atau membuat template perangkat untuk perangkat ini. Untuk mempelajari selengkapnya, lihat Menetapkan perangkat ke templat perangkat.
  • Buat instans DeviceClient untuk terhubung ke 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;
}

Metode utama kemudian membuat instans TemperatureControllerSample dan memanggil metode PerformOperationsAsync untuk menangani interaksi dengan IoT Central.

Pada TemperatureControllerSample.cs, metode PerformOperationsAsync:

  • Mengatur penangan untuk perintah reboot pada komponen default.
  • Mengatur penangan untuk perintah getMaxMinReport pada dua komponen termostat.
  • Mengatur penangan untuk menerima pembaruan properti suhu target pada dua komponen termostat.
  • Mengirimkan pembaruan properti informasi perangkat awal.
  • Secara berkala mengirimkan telemetri suhu dari dua komponen termostat.
  • Secara berkala mengirimkan rangkaian aktif telemetri dari komponen default.
  • Mengirimkan suhu maksimum sejak reboot terakhir setiap kali suhu maksimum baru tercapai di dua komponen termostat.
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);
  }
}

Metode SendTemperatureAsync menunjukkan cara perangkat mengirim telemetri suhu dari komponen ke IoT Central. Metode SendTemperatureTelemetryAsync menggunakan kelas PnpConvention untuk membuat pesan:

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

Metode UpdateMaxTemperatureSinceLastRebootAsync mengirimkan pembaruan properti maxTempSinceLastReboot ke IoT Central. Metode ini menggunakan PnpConvention kelas untuk membuat patch:

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

Metode TargetTemperatureUpdateCallbackAsync menangani pembaruan properti suhu target bisa-tulis dari IoT Central. Metode ini menggunakan kelas PnpConvention untuk membaca pesan pembaruan properti dan menyusun respons:

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

Metode HandleMaxMinReportCommand menangani perintah untuk komponen yang dipanggil dari 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)
    {
        // ...
    }
}

Mendapatkan informasi koneksi

Ketika Anda menjalankan aplikasi perangkat sampel dalam tutorial ini nanti, Anda memerlukan nilai konfigurasi berikut:

  • Cakupan ID: Di aplikasi IoT Central Anda, navigasikan ke grup koneksi Perangkat Izin>. Catat nilai lingkup ID.
  • Kunci utama grup: Di aplikasi IoT Central Anda, navigasikan ke Grup > koneksi Perangkat Izin > SAS-IoT-Devices. Catat nilai Kunci primer tanda tangan akses bersama.

Gunakan Azure Cloud Shell untuk menghasilkan kunci perangkat dari kunci utama grup yang Anda ambil:

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

Catat kunci perangkat yang dihasilkan, Anda gunakan nanti dalam tutorial ini.

Catatan

Untuk menjalankan sampel ini, Anda tidak perlu mendaftarkan perangkat terlebih dahulu di aplikasi IoT Central Anda. Sampel menggunakan kemampuan IoT Central untuk mendaftarkan perangkat secara otomatis saat tersambung untuk pertama kalinya.

Menjalankan kode

Catatan

Siapkan TemperatureController sebagai proyek startup sebelum Anda menjalankan kode.

Untuk menjalankan aplikasi sampel di Visual Studio:

  1. Di Penjelajah Solusi, pilih file proyek PnpDeviceSamples > TemperatureController.

  2. Navigasi ke Debug Properti > Pengontrol Suhu Proyek>. Kemudian, tambahkan variabel lingkungan berikut ke proyek:

    Nama Nilai
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Nilai cakupan ID yang Anda catat sebelumnya.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY Nilai kunci perangkat yang dibuat yang Anda catat sebelumnya.

Anda sekarang dapat menjalankan dan debug sampel di Visual Studio.

Output berikut menunjukkan perangkat mendaftar dan menghubungkan ke IoT Central. Sampel mulai mengirimkan 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.

Sebagai operator di aplikasi Azure IoT Central, Anda dapat:

  • Melihat telemetri yang dikirim oleh dua komponen termostat pada halaman Gambaran Umum:

    Screenshot that shows the device overview page.

  • Lihat properti perangkat pada halaman Tentang. Halaman ini menampilkan properti dari komponen informasi perangkat dan dua komponen termostat:

    Screenshot that shows the device properties view.

Menyesuaikan templat perangkat

Sebagai pengembang solusi, Anda dapat menyesuaikan templat perangkat yang dibuat IoT Central secara otomatis ketika perangkat pengontrol suhu terhubung.

Untuk menambahkan properti cloud untuk menyimpan nama pelanggan yang terkait dengan perangkat:

  1. Di aplikasi IoT Central Anda, navigasikan ke templat perangkat Pengontrol Suhu pada halaman Templat perangkat.

  2. Dalam model Pengontrol Suhu, pilih +Tambahkan kemampuan.

  3. Masukkan Nama pelanggan sebagai Nama tampilan, pilih Properti cloud sebagai jenis kemampuan, perluas entri dan pilih String sebagai Skema. Kemudian pilih Simpan.

Untuk menyesuaikan cara perintah Mendapatkan laporan Max-Min ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk getMaxMinReport (termostat1), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat1.

  3. Untuk getMaxMinReport (termostat2), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat2.

  4. Pilih Simpan.

Untuk mengkustomisasi cara Suhu Target properti yang dapat ditulis ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk targetTemperature (thermostat1), ganti Suhu Target dengan Suhu Target (1).

  3. Untuk targetTemperature (thermostat2), ganti Suhu Target dengan Suhu Target (2).

  4. Pilih Simpan.

Komponen termostat dalam model Pengontrol Suhu termasuk properti yang dapat ditulis Suhu Target, templat perangkat termasuk properti cloud Nama Pelanggan. Membuat tampilan yang dapat digunakan operator untuk mengedit properti ini:

  1. Pilih Tampilan lalu pilih petak Pengeditan perangkat dan data cloud.

  2. Masukkan Properti sebagai nama formulir.

  3. Pilih properti Suhu Target (1), Suhu Target (2), dan Nama Pelanggan. Kemudian pilih Tambahkan bagian.

  4. Simpan perubahan.

Screenshot that shows a view for updating property values.

Menerbitkan templat perangkat

Sebelum operator dapat melihat dan menggunakan kustomisasi yang Anda buat, Anda harus menerbitkan templat perangkat.

Dari templat perangkat Termostat, pilih Terbitkan. Pada halaman Terbitkan template perangkat ini ke panel aplikasi, pilih Terbitkan.

Operator sekarang dapat menggunakan tampilan Properti untuk memperbarui nilai properti, dan perintah panggilan yang disebut Get thermostat1 status report dan Get thermostat2 status report di halaman perintah perangkat:

  • Perbarui nilai properti yang dapat ditulis pada halaman Properti:

    Screenshot that shows updating the device properties.

  • Panggil perintah dari halaman Perintah. Jika Anda menjalankan perintah laporan status, pilih tanggal dan waktu untuk parameter Sejak sebelum Anda menjalankannya:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Anda dapat melihat bagaimana perangkat merespons perintah dan pembaruan properti:

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

...

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

Browse code

Prasyarat

Untuk menyelesaikan langkah-langkah dalam artikel ini, Anda memerlukan sumber daya berikut:

  • Mesin pengembangan dengan Java SE Development Kit 8 atau yang lebih baru. Untuk informasi selengkapnya, lihat Menginstal JDK.

  • Apache Maven 3.

  • Salinan lokal repositori GitHub Microsoft Azure IoT SDK for Java yang berisi kode contoh. Gunakan tautan ini untuk mengunduh salinan repositori: Unduh ZIP. Kemudian unzip file ke lokasi yang sesuai di komputer lokal Anda.

Mengulas kode

Dalam salinan Microsoft Azure IoT SDK untuk Java yang Anda unduh sebelumnya, buka file 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 di editor teks.

Sampel mengimplementasikan model Temperature Controller Digital Twin Definition Language multi-komponen.

Saat Anda menjalankan sampel untuk terhubung ke IoT Central, sampel tersebut menggunakan Device Provisioning Service (DPS) untuk mendaftarkan perangkat dan menghasilkan string koneksi. Sampel mengambil informasi koneksi DPS yang dibutuhkan dari lingkungan baris perintah.

Metode main:

  • Memanggil initializeAndProvisionDevice untuk menyetel dtmi:com:example:TemperatureController;2 model ID, menggunakan DPS untuk menyediakan dan mendaftarkan perangkat, membuat instance DeviceClient, dan menghubungkan ke aplikasi IoT Central Anda. IoT Central menggunakan ID model untuk mengidentifikasi atau membuat template perangkat untuk perangkat ini. Untuk mempelajari selengkapnya, lihat Menetapkan perangkat ke templat perangkat.
  • Membuat pengendali perintah untuk perintah getMaxMinReport dan reboot.
  • Membuat penangan pembaruan properti untuk properti targetTemperature yang dapat ditulis.
  • Mengirim nilai awal untuk properti di antarmuka Informasi Perangkat dan properti Memori Perangkat dan Nomor Seri.
  • Memulai utas untuk mengirim telemetri suhu dari dua termostat dan memperbarui properti maxTempSinceLastReboot setiap lima detik.
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();
}

Metode initializeAndProvisionDevice menunjukkan bagaimana perangkat menggunakan DPS untuk mendaftar dan terhubung ke IoT Central. Payload mencakup ID model yang digunakan IoT Central untuk menetapkan perangkat ke templat perangkat:

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

Metode sendTemperatureTelemetry menunjukkan cara perangkat mengirim telemetri suhu dari komponen ke IoT Central. Metode ini menggunakan PnpConvention kelas untuk membuat pesan:

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

Metode updateMaxTemperatureSinceLastReboot mengirimkan maxTempSinceLastReboot pembaruan properti dari komponen ke IoT Central. Metode ini menggunakan PnpConvention kelas untuk membuat patch:

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

Kelas TargetTemperatureUpdateCallback berisi metode onPropertyChanged untuk menangani pembaruan properti yang dapat ditulis ke komponen dari IoT Central. Metode ini menggunakan PnpConvention kelas untuk membuat respons:

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

Kelas MethodCallback berisi metode onMethodInvoked untuk menangani perintah komponen yang dipanggil dari 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);
        }
    }
}

Mendapatkan informasi koneksi

Ketika Anda menjalankan aplikasi perangkat sampel dalam tutorial ini nanti, Anda memerlukan nilai konfigurasi berikut:

  • Cakupan ID: Di aplikasi IoT Central Anda, navigasikan ke grup koneksi Perangkat Izin>. Catat nilai lingkup ID.
  • Kunci utama grup: Di aplikasi IoT Central Anda, navigasikan ke Grup > koneksi Perangkat Izin > SAS-IoT-Devices. Catat nilai Kunci primer tanda tangan akses bersama.

Gunakan Azure Cloud Shell untuk menghasilkan kunci perangkat dari kunci utama grup yang Anda ambil:

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

Catat kunci perangkat yang dihasilkan, Anda gunakan nanti dalam tutorial ini.

Catatan

Untuk menjalankan sampel ini, Anda tidak perlu mendaftarkan perangkat terlebih dahulu di aplikasi IoT Central Anda. Sampel menggunakan kemampuan IoT Central untuk mendaftarkan perangkat secara otomatis saat tersambung untuk pertama kalinya.

Di Windows, navigasikan ke folder akar repositori Azure IoT SDK untuk Java yang Anda unduh.

Jalankan perintah berikut untuk membangun aplikasi sampel:

mvn install -T 2C -DskipTests

Menjalankan kode

Untuk menjalankan aplikasi sampel, buka lingkungan baris perintah dan navigasikan ke folder azure-iot-sdk-java /iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample folder yang berisi folder src dengan file sampel TemperatureController.java .

Atur variabel lingkungan untuk mengonfigurasi sampel. Cuplikan berikut menunjukkan bagaimana mengatur variabel lingkungan pada perintah Windows. Jika Anda menggunakan shell bash, ganti perintah set dengan perintah 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

Jalankan sampel:

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

Output berikut menunjukkan perangkat mendaftar dan menghubungkan ke IoT Central. Sampel mulai mengirimkan 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 [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.774 DEBUG TemperatureController:156 - Set handler to receive "targetTemperature" updates.
2021-03-30 15:33:44.775 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.779 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/methods/POST/#
2021-03-30 15:33:44.793 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.794 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [417d659a-7324-43fa-84eb-8a3f3d07963c] Message Id [55532cad-8a5a-489f-9aa8-8f0e5bc21541] Request Id [0] Device Operation Type [DEVICE_OPERATION_TWIN_GET_REQUEST] )
2021-03-30 15:33:44.819 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [d46a0d8a-8a18-4014-abeb-768bd9b17ad2] Message Id [780abc81-ce42-4e5f-aa80-e4785883604e] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.881 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic $iothub/methods/POST/# was acknowledged
2021-03-30 15:33:44.882 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.882 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/twin/res/#
2021-03-30 15:33:44.893 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [a77b1c02-f043-4477-b610-e31a774772c0] Message Id [2e2f6bee-c480-42cf-ac31-194118930846] Request Id [1] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.904 DEBUG TemperatureController:423 - Property: Update - component = "deviceInformation" is COMPLETED.
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [bbb7e3cf-3550-4fdf-90f9-0787740f028a] Message Id [e06ac385-ae0d-46dd-857a-d9725707527a] )
2021-03-30 15:33:44.915 DEBUG TemperatureController:434 - Telemetry: Sent - {"workingSet": 1024.0KiB }
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [6dbef765-cc9a-4e72-980a-2fe5b0cd77e1] Message Id [49bbad33-09bf-417a-9d6e-299ba7b7c562] Request Id [2] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.916 DEBUG TemperatureController:442 - Property: Update - {"serialNumber": SR-123456} is COMPLETED
2021-03-30 15:33:44.927 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [86787c32-87a5-4c49-9083-c7f2b17446a7] Message Id [0a45fa0c-a467-499d-b214-9bb5995772ba] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id 0a45fa0c-a467-499d-b214-9bb5995772ba.

Sebagai operator di aplikasi Azure IoT Central, Anda dapat:

  • Melihat telemetri yang dikirim oleh dua komponen termostat pada halaman Gambaran Umum:

    Screenshot that shows the device overview page.

  • Lihat properti perangkat pada halaman Tentang. Halaman ini menampilkan properti dari komponen informasi perangkat dan dua komponen termostat:

    Screenshot that shows the device properties view.

Menyesuaikan templat perangkat

Sebagai pengembang solusi, Anda dapat menyesuaikan templat perangkat yang dibuat IoT Central secara otomatis ketika perangkat pengontrol suhu terhubung.

Untuk menambahkan properti cloud untuk menyimpan nama pelanggan yang terkait dengan perangkat:

  1. Di aplikasi IoT Central Anda, navigasikan ke templat perangkat Pengontrol Suhu pada halaman Templat perangkat.

  2. Dalam model Pengontrol Suhu, pilih +Tambahkan kemampuan.

  3. Masukkan Nama pelanggan sebagai Nama tampilan, pilih Properti cloud sebagai jenis kemampuan, perluas entri dan pilih String sebagai Skema. Kemudian pilih Simpan.

Untuk menyesuaikan cara perintah Mendapatkan laporan Max-Min ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk getMaxMinReport (termostat1), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat1.

  3. Untuk getMaxMinReport (termostat2), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat2.

  4. Pilih Simpan.

Untuk mengkustomisasi cara Suhu Target properti yang dapat ditulis ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk targetTemperature (thermostat1), ganti Suhu Target dengan Suhu Target (1).

  3. Untuk targetTemperature (thermostat2), ganti Suhu Target dengan Suhu Target (2).

  4. Pilih Simpan.

Komponen termostat dalam model Pengontrol Suhu termasuk properti yang dapat ditulis Suhu Target, templat perangkat termasuk properti cloud Nama Pelanggan. Membuat tampilan yang dapat digunakan operator untuk mengedit properti ini:

  1. Pilih Tampilan lalu pilih petak Pengeditan perangkat dan data cloud.

  2. Masukkan Properti sebagai nama formulir.

  3. Pilih properti Suhu Target (1), Suhu Target (2), dan Nama Pelanggan. Kemudian pilih Tambahkan bagian.

  4. Simpan perubahan.

Screenshot that shows a view for updating property values.

Menerbitkan templat perangkat

Sebelum operator dapat melihat dan menggunakan kustomisasi yang Anda buat, Anda harus menerbitkan templat perangkat.

Dari templat perangkat Termostat, pilih Terbitkan. Pada halaman Terbitkan template perangkat ini ke panel aplikasi, pilih Terbitkan.

Operator sekarang dapat menggunakan tampilan Properti untuk memperbarui nilai properti, dan perintah panggilan yang disebut Get thermostat1 status report dan Get thermostat2 status report di halaman perintah perangkat:

  • Perbarui nilai properti yang dapat ditulis pada halaman Properti:

    Screenshot that shows updating the device properties.

  • Panggil perintah dari halaman Perintah. Jika Anda menjalankan perintah laporan status, pilih tanggal dan waktu untuk parameter Sejak sebelum Anda menjalankannya:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Anda dapat melihat bagaimana perangkat merespons perintah dan pembaruan properti:

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


...

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

Browse code

Prasyarat

Untuk menyelesaikan langkah-langkah dalam artikel ini, Anda memerlukan sumber daya berikut:

  • Komputer pengembangan dengan Node.js versi 6 atau yang lebih baru diinstal. Anda dapat menjalankan node --version di baris perintah untuk memeriksa versi Anda. Instruksi dalam tutorial ini mengasumsikan Anda menjalankan perintah node pada perintah Windows. Namun, Anda dapat menggunakan Node.js di banyak sistem operasi lain.

  • Salinan lokal repositori GitHub Microsoft Azure IoT SDK untuk Node.js yang berisi kode sampel. Gunakan tautan ini untuk mengunduh salinan repositori: Unduh ZIP. Kemudian unzip file ke lokasi yang sesuai di komputer lokal Anda.

Mengulas kode

Dalam salinan Microsoft Azure IoT SDK untuk Node.js anda unduh sebelumnya, buka file azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js di editor teks.

Sampel mengimplementasikan model Temperature Controller Digital Twin Definition Language multi-komponen.

Saat Anda menjalankan sampel untuk terhubung ke IoT Central, sampel tersebut menggunakan Device Provisioning Service (DPS) untuk mendaftarkan perangkat dan menghasilkan string koneksi. Sampel mengambil informasi koneksi DPS yang dibutuhkan dari lingkungan baris perintah.

Metode main:

  • Membuat objek client dan mengatur ID model dtmi:com:example:TemperatureController;2 sebelum membuka koneksi. IoT Central menggunakan ID model untuk mengidentifikasi atau membuat template perangkat untuk perangkat ini. Untuk mempelajari selengkapnya, lihat Menetapkan perangkat ke templat perangkat.
  • Membuat penangan perintah untuk tiga perintah.
  • Memulai perulangan untuk setiap komponen termostat guna mengirimkan telemetri suhu setiap 5 detik.
  • Memulai perulangan untuk komponen default guna mengirimkan rangkaian aktif telemetri ukuran setiap 6 detik.
  • Mengirimkan properti maxTempSinceLastReboot untuk setiap komponen termostat.
  • Mengirimkan properti informasi perangkat.
  • Membuat penangan properti bisa-tulis untuk tiga komponen.
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());
  }
}

Fungsi provisionDevice menunjukkan bagaimana perangkat menggunakan DPS untuk mendaftar dan terhubung ke IoT Central. Payload mencakup ID model yang digunakan IoT Central untuk Menetapkan perangkat ke templat perangkat:

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

Fungsi sendTelemetry menunjukkan cara perangkat mengirimkan telemetri suhu ke IoT Central. Untuk telemetri dari komponen, ia menambahkan properti yang disebut $.sub dengan nama komponen:

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

Metode main menggunakan metode bantuan yang disebut helperCreateReportedPropertiesPatch untuk membuat pesan pembaruan properti. Metode ini mengambil parameter opsional untuk menentukan komponen yang mengirim properti:

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

Metode main menggunakan metode berikut untuk menangani pembaruan pada properti bisa-tulis dari IoT Central. Perhatikan bagaimana metode membangun respons dengan versi dan kode status:

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

Metode main menggunakan metode berikut untuk menangani perintah dari 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());
  }
};

Mendapatkan informasi koneksi

Ketika Anda menjalankan aplikasi perangkat sampel dalam tutorial ini nanti, Anda memerlukan nilai konfigurasi berikut:

  • Cakupan ID: Di aplikasi IoT Central Anda, navigasikan ke grup koneksi Perangkat Izin>. Catat nilai lingkup ID.
  • Kunci utama grup: Di aplikasi IoT Central Anda, navigasikan ke Grup > koneksi Perangkat Izin > SAS-IoT-Devices. Catat nilai Kunci primer tanda tangan akses bersama.

Gunakan Azure Cloud Shell untuk menghasilkan kunci perangkat dari kunci utama grup yang Anda ambil:

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

Catat kunci perangkat yang dihasilkan, Anda gunakan nanti dalam tutorial ini.

Catatan

Untuk menjalankan sampel ini, Anda tidak perlu mendaftarkan perangkat terlebih dahulu di aplikasi IoT Central Anda. Sampel menggunakan kemampuan IoT Central untuk mendaftarkan perangkat secara otomatis saat tersambung untuk pertama kalinya.

Menjalankan kode

Untuk menjalankan aplikasi sampel, buka lingkungan baris perintah dan navigasikan ke folder azure-iot-sdk-node /device/samples/javascript yang berisi file sampel pnp_temperature_controller.js .

Atur variabel lingkungan untuk mengonfigurasi sampel. Cuplikan berikut menunjukkan bagaimana mengatur variabel lingkungan pada perintah Windows. Jika Anda menggunakan shell bash, ganti perintah set dengan perintah 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

Instal paket yang diperlukan:

npm install

Jalankan sampel:

node pnp_temperature_controller.js

Output berikut menunjukkan perangkat mendaftar dan menghubungkan ke IoT Central. Sampel kemudian mengirimkan properti maxTempSinceLastReboot dari dua komponen termostat sebelum mulai mengirim telemetri:

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

Sebagai operator di aplikasi Azure IoT Central, Anda dapat:

  • Melihat telemetri yang dikirim oleh dua komponen termostat pada halaman Gambaran Umum:

    Screenshot that shows the device overview page.

  • Lihat properti perangkat pada halaman Tentang. Halaman ini menampilkan properti dari komponen informasi perangkat dan dua komponen termostat:

    Screenshot that shows the device properties view.

Menyesuaikan templat perangkat

Sebagai pengembang solusi, Anda dapat menyesuaikan templat perangkat yang dibuat IoT Central secara otomatis ketika perangkat pengontrol suhu terhubung.

Untuk menambahkan properti cloud untuk menyimpan nama pelanggan yang terkait dengan perangkat:

  1. Di aplikasi IoT Central Anda, navigasikan ke templat perangkat Pengontrol Suhu pada halaman Templat perangkat.

  2. Dalam model Pengontrol Suhu, pilih +Tambahkan kemampuan.

  3. Masukkan Nama pelanggan sebagai Nama tampilan, pilih Properti cloud sebagai jenis kemampuan, perluas entri dan pilih String sebagai Skema. Kemudian pilih Simpan.

Untuk menyesuaikan cara perintah Mendapatkan laporan Max-Min ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk getMaxMinReport (termostat1), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat1.

  3. Untuk getMaxMinReport (termostat2), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat2.

  4. Pilih Simpan.

Untuk mengkustomisasi cara Suhu Target properti yang dapat ditulis ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk targetTemperature (thermostat1), ganti Suhu Target dengan Suhu Target (1).

  3. Untuk targetTemperature (thermostat2), ganti Suhu Target dengan Suhu Target (2).

  4. Pilih Simpan.

Komponen termostat dalam model Pengontrol Suhu termasuk properti yang dapat ditulis Suhu Target, templat perangkat termasuk properti cloud Nama Pelanggan. Membuat tampilan yang dapat digunakan operator untuk mengedit properti ini:

  1. Pilih Tampilan lalu pilih petak Pengeditan perangkat dan data cloud.

  2. Masukkan Properti sebagai nama formulir.

  3. Pilih properti Suhu Target (1), Suhu Target (2), dan Nama Pelanggan. Kemudian pilih Tambahkan bagian.

  4. Simpan perubahan.

Screenshot that shows a view for updating property values.

Menerbitkan templat perangkat

Sebelum operator dapat melihat dan menggunakan kustomisasi yang Anda buat, Anda harus menerbitkan templat perangkat.

Dari templat perangkat Termostat, pilih Terbitkan. Pada halaman Terbitkan template perangkat ini ke panel aplikasi, pilih Terbitkan.

Operator sekarang dapat menggunakan tampilan Properti untuk memperbarui nilai properti, dan perintah panggilan yang disebut Get thermostat1 status report dan Get thermostat2 status report di halaman perintah perangkat:

  • Perbarui nilai properti yang dapat ditulis pada halaman Properti:

    Screenshot that shows updating the device properties.

  • Panggil perintah dari halaman Perintah. Jika Anda menjalankan perintah laporan status, pilih tanggal dan waktu untuk parameter Sejak sebelum Anda menjalankannya:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Anda dapat melihat bagaimana perangkat merespons perintah dan pembaruan properti. Perintah getMaxMinReport terdapat di komponen thermostat2, perintah reboot terdapat di komponen default. Properti targetTemperature bisa-tulis disetel untuk thermostat2 komponen:

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

...

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

...

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

Browse code

Prasyarat

Untuk menyelesaikan langkah-langkah dalam artikel ini, Anda memerlukan sumber daya berikut:

  • Mesin pengembangan dengan Python terinstal. Periksa Azure IoT Python SDK untuk persyaratan versi Python saat ini. Anda dapat menjalankan python --version pada baris perintah untuk memeriksa versi Anda. Phyton tersedia untuk berbagai macam sistem operasi. Instruksi dalam tutorial ini mengasumsikan Anda menjalankan perintah python pada perintah Windows.

  • Salinan lokal repositori GitHub Microsoft Azure IoT SDK untuk Python yang berisi kode sampel. Gunakan tautan ini untuk mengunduh salinan repositori: Unduh ZIP. Kemudian unzip file ke lokasi yang sesuai di komputer lokal Anda.

Mengulas kode

Dalam salinan Microsoft Azure IoT SDK untuk Python yang Anda unduh sebelumnya, buka file azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py di editor teks.

Sampel mengimplementasikan model Temperature Controller Digital Twin Definition Language multi-komponen.

Saat Anda menjalankan sampel untuk terhubung ke IoT Central, sampel tersebut menggunakan Device Provisioning Service (DPS) untuk mendaftarkan perangkat dan menghasilkan string koneksi. Sampel mengambil informasi koneksi DPS yang dibutuhkan dari lingkungan baris perintah.

Fungsi main:

  • Menggunakan DPS untuk memprovisikan perangkat. Informasi provisi menyertakan ID model. IoT Central menggunakan ID model untuk mengidentifikasi atau membuat template perangkat untuk perangkat ini. Untuk mempelajari selengkapnya, lihat Menetapkan perangkat ke templat perangkat.
  • Membuat objek Device_client dan mengatur ID model dtmi:com:example:TemperatureController;2 sebelum membuka koneksi.
  • Mengirim nilai properti awal ke IoT Central. Hal ini menggunakan pnp_helper untuk membuat patch.
  • Membuat pendengar untuk perintah getMaxMinReport dan reboot. Setiap komponen termostat memiliki perintah getMaxMinReport sendiri.
  • Membuat pendengar properti, untuk mendengarkan pembaruan properti bisa-tulis.
  • Memulai perulangan untuk mengirim telemetri suhu dari dua komponen termostat dan bekerja mengatur telemetri dari komponen default setiap 8 detik.
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())

    # ...

Fungsi provision_device menggunakan DPS untuk memprovisikan perangkat dan mendaftarkannya ke IoT Central. Fungsi ini mencakup ID model perangkat, yang digunakan IoT Central untuk menetapkan perangkat ke templat perangkat, dalam payload provisi:

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

Fungsi execute_command_listener menangani permintaan perintah, menjalankan fungsi max_min_handler saat perangkat menerima perintah getMaxMinReport untuk komponen termostat dan fungsi reboot_handler saat perangkat menerima perintah reboot. Hal ini menggunakan modul pnp_helper ​​untuk membuat respons:

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 menangani pembaruan properti bisa-tulis seperti targetTemperature untuk komponen termostat dan membuat respons JSON. Hal ini menggunakan modul pnp_helper ​​untuk membuat respons:

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)

Fungsi send_telemetry_from_temp_controller mengirimkan pesan telemetri dari komponen termostat ke IoT Central. Hal ini menggunakan modul pnp_helper ​​untuk membuat pesan:

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)

Mendapatkan informasi koneksi

Ketika Anda menjalankan aplikasi perangkat sampel dalam tutorial ini nanti, Anda memerlukan nilai konfigurasi berikut:

  • Cakupan ID: Di aplikasi IoT Central Anda, navigasikan ke grup koneksi Perangkat Izin>. Catat nilai lingkup ID.
  • Kunci utama grup: Di aplikasi IoT Central Anda, navigasikan ke Grup > koneksi Perangkat Izin > SAS-IoT-Devices. Catat nilai Kunci primer tanda tangan akses bersama.

Gunakan Azure Cloud Shell untuk menghasilkan kunci perangkat dari kunci utama grup yang Anda ambil:

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

Catat kunci perangkat yang dihasilkan, Anda gunakan nanti dalam tutorial ini.

Catatan

Untuk menjalankan sampel ini, Anda tidak perlu mendaftarkan perangkat terlebih dahulu di aplikasi IoT Central Anda. Sampel menggunakan kemampuan IoT Central untuk mendaftarkan perangkat secara otomatis saat tersambung untuk pertama kalinya.

Menjalankan kode

Untuk menjalankan aplikasi sampel, buka lingkungan baris perintah dan navigasikan ke folder azure-iot-sdk-python-2 /samples/pnp yang berisi file sampel temp_controller_with_thermostats.py .

Atur variabel lingkungan untuk mengonfigurasi sampel. Cuplikan berikut menunjukkan bagaimana mengatur variabel lingkungan pada perintah Windows. Jika Anda menggunakan shell bash, ganti perintah set dengan perintah 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

Instal paket yang diperlukan:

pip install azure-iot-device

Jalankan sampel:

python temp_controller_with_thermostats.py

Output berikut menunjukkan perangkat mendaftar dan menghubungkan ke IoT Central. Sampel mengirimkan properti maxTempSinceLastReboot dari dua komponen termostat sebelum mulai mengirim telemetri:

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}

Sebagai operator di aplikasi Azure IoT Central, Anda dapat:

  • Melihat telemetri yang dikirim oleh dua komponen termostat pada halaman Gambaran Umum:

    Screenshot that shows the device overview page.

  • Lihat properti perangkat pada halaman Tentang. Halaman ini menampilkan properti dari komponen informasi perangkat dan dua komponen termostat:

    Screenshot that shows the device properties view.

Menyesuaikan templat perangkat

Sebagai pengembang solusi, Anda dapat menyesuaikan templat perangkat yang dibuat IoT Central secara otomatis ketika perangkat pengontrol suhu terhubung.

Untuk menambahkan properti cloud untuk menyimpan nama pelanggan yang terkait dengan perangkat:

  1. Di aplikasi IoT Central Anda, navigasikan ke templat perangkat Pengontrol Suhu pada halaman Templat perangkat.

  2. Dalam model Pengontrol Suhu, pilih +Tambahkan kemampuan.

  3. Masukkan Nama pelanggan sebagai Nama tampilan, pilih Properti cloud sebagai jenis kemampuan, perluas entri dan pilih String sebagai Skema. Kemudian pilih Simpan.

Untuk menyesuaikan cara perintah Mendapatkan laporan Max-Min ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk getMaxMinReport (termostat1), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat1.

  3. Untuk getMaxMinReport (termostat2), ganti laporan Get Max-Min. dengan dapatkan laporan status termostat2.

  4. Pilih Simpan.

Untuk mengkustomisasi cara Suhu Target properti yang dapat ditulis ditampilkan di aplikasi IoT Central Anda:

  1. Navigasikan ke templat perangkat Pengontrol Suhu di halaman Templat perangkat .

  2. Untuk targetTemperature (thermostat1), ganti Suhu Target dengan Suhu Target (1).

  3. Untuk targetTemperature (thermostat2), ganti Suhu Target dengan Suhu Target (2).

  4. Pilih Simpan.

Komponen termostat dalam model Pengontrol Suhu termasuk properti yang dapat ditulis Suhu Target, templat perangkat termasuk properti cloud Nama Pelanggan. Membuat tampilan yang dapat digunakan operator untuk mengedit properti ini:

  1. Pilih Tampilan lalu pilih petak Pengeditan perangkat dan data cloud.

  2. Masukkan Properti sebagai nama formulir.

  3. Pilih properti Suhu Target (1), Suhu Target (2), dan Nama Pelanggan. Kemudian pilih Tambahkan bagian.

  4. Simpan perubahan.

Screenshot that shows a view for updating property values.

Menerbitkan templat perangkat

Sebelum operator dapat melihat dan menggunakan kustomisasi yang Anda buat, Anda harus menerbitkan templat perangkat.

Dari templat perangkat Termostat, pilih Terbitkan. Pada halaman Terbitkan template perangkat ini ke panel aplikasi, pilih Terbitkan.

Operator sekarang dapat menggunakan tampilan Properti untuk memperbarui nilai properti, dan perintah panggilan yang disebut Get thermostat1 status report dan Get thermostat2 status report di halaman perintah perangkat:

  • Perbarui nilai properti yang dapat ditulis pada halaman Properti:

    Screenshot that shows updating the device properties.

  • Panggil perintah dari halaman Perintah. Jika Anda menjalankan perintah laporan status, pilih tanggal dan waktu untuk parameter Sejak sebelum Anda menjalankannya:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Anda dapat melihat bagaimana perangkat merespons perintah dan pembaruan properti:

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

Menampilkan data mentah

Anda dapat menggunakan tampilan Data mentah untuk memeriksa data mentah yang dikirim perangkat Anda ke IoT Central:

Screenshot that shows the raw data view.

Pada tampilan ini, Anda dapat memilih kolom yang akan ditampilkan dan mengatur rentang waktu untuk ditampilkan. Kolom Data yang tidak dimodifikasi memperlihatkan data perangkat yang tidak sesuai dengan definisi properti atau telemetri apa pun di templat perangkat.

Membersihkan sumber daya

Jika Anda tidak berencana untuk menyelesaikan mulai cepat atau tutorial IoT Central lebih lanjut, Anda dapat menghapus aplikasi IoT Central Anda:

  1. Di aplikasi IoT Central Anda, navigasikan ke Manajemen Aplikasi>.
  2. Pilih Hapus lalu konfirmasi tindakan Anda.

Langkah berikutnya

Jika Anda lebih suka melanjutkan melalui rangkaian tutorial IoT Central dan mempelajari lebih lanjut tentang membangun solusi IoT Central, lihat: