Menyambungkan perangkat Raspberry Pi Anda ke akselerator solusi Pemantauan Jarak Jauh (C)

Dalam tutorial ini, Anda mengimplementasikan perangkat Chiller yang mengirim telemetri berikut ke akselerator solusi Pemantauan Jarak Jauh:

  • Suhu
  • Tekanan
  • Kelembapan

Untuk kesederhanaan, kode menghasilkan nilai telemetri sampel untuk Chiller. Anda dapat memperluas sampel dengan menghubungkan sensor nyata ke perangkat Anda dan mengirim telemetri nyata.

Perangkat sampel juga:

  • Mengirim metadata ke solusi untuk menjelaskan kemampuannya.
  • Merespons tindakan yang dipicu dari halaman Perangkat dalam solusi.
  • Merespons perubahan konfigurasi yang dikirim dari halaman Perangkat dalam solusi.

Untuk menyelesaikan tutorial ini, Anda memerlukan akun Azure aktif. Jika belum memiliki akun, Anda dapat membuat akun coba gratis hanya dalam beberapa menit. Untuk detailnya, lihat Coba Gratis Azure.

Sebelum Anda memulai

Sebelum Anda menulis kode apa pun untuk perangkat Anda, sebarkan akselerator solusi Pemantauan Jarak Jauh Anda dan tambahkan perangkat nyata baru ke solusi.

Menyebarkan akselerator solusi Pemantauan Jarak Jauh Anda

Perangkat Chiller yang Anda buat dalam tutorial ini mengirimkan data ke instans akselerator solusi Pemantauan Jarak Jauh . Jika Anda belum menyediakan akselerator solusi Pemantauan Jarak Jauh di akun Azure Anda, lihat Menyebarkan akselerator solusi Pemantauan Jarak Jauh

Saat proses penyebaran untuk solusi Pemantauan Jarak Jauh selesai, klik Luncurkan untuk membuka dasbor solusi di browser Anda.

Dasbor solusi

Menambahkan perangkat Anda ke solusi Pemantauan Jarak Jauh

Catatan

Jika Anda telah menambahkan perangkat dalam solusi, Anda dapat melewati langkah ini. Namun, langkah berikutnya memerlukan string koneksi perangkat Anda. Anda dapat mengambil string koneksi perangkat dari portal Azure atau menggunakan alat CLI az iot.

Agar perangkat terhubung ke akselerator solusi, perangkat harus mengidentifikasi dirinya sendiri untuk IoT Hub menggunakan kredensial yang valid. Anda memiliki kesempatan untuk menyimpan string koneksi perangkat yang berisi kredensial ini saat Anda menambahkan perangkat ke solusi. Anda menyertakan string koneksi perangkat di aplikasi klien Anda nanti dalam tutorial ini.

Untuk menambahkan perangkat ke solusi Pemantauan Jarak Jauh Anda, selesaikan langkah-langkah berikut di halaman Penjelajah Perangkat dalam solusi:

  1. Pilih + Perangkat baru, lalu pilih Nyata sebagai Jenis perangkat:

    Menambahkan perangkat nyata

  2. Masukkan Physical-chiller sebagai ID Perangkat. Pilih opsi Kunci Konten dan Buat kunci secara otomatis :

    Pilih opsi perangkat

  3. Pilih Terapkan. Kemudian catat nilai kunci primerID Perangkat, Kunci Primer, dan String koneksi:

    Mengambil kredensial

Anda sekarang telah menambahkan perangkat nyata ke akselerator solusi Pemantauan Jarak Jauh dan mencatat string koneksi perangkatnya. Di bagian berikut, Anda menerapkan aplikasi klien yang menggunakan string koneksi perangkat untuk menyambungkan ke solusi Anda.

Aplikasi klien mengimplementasikan model perangkat Chiller bawaan. Model perangkat akselerator solusi menentukan hal berikut tentang perangkat:

  • Properti yang dilaporkan perangkat ke solusi. Misalnya, perangkat Chiller melaporkan informasi tentang firmware dan lokasinya.
  • Jenis telemetri yang dikirim perangkat ke solusi. Misalnya, perangkat Chiller mengirimkan nilai suhu, kelembaban, dan tekanan.
  • Metode yang dapat Anda jadwalkan dari solusi untuk dijalankan pada perangkat. Misalnya, perangkat Chiller harus menerapkan metode Reboot, FirmwareUpdate, EmergencyValveRelease, dan IncreasePressure .

Tutorial ini menunjukkan kepada Anda cara menghubungkan perangkat nyata ke akselerator solusi Pemantauan Jarak Jauh. Seperti kebanyakan aplikasi yang disematkan yang berjalan pada perangkat yang dibatasi, kode klien untuk aplikasi perangkat Raspberry Pi ditulis dalam C. Dalam tutorial ini, Anda membangun aplikasi pada Raspberry Pi yang menjalankan OS Raspbian.

Jika Anda lebih suka mensimulasikan perangkat, lihat Membuat dan menguji perangkat simulasi baru.

Perangkat keras yang diperlukan

Komputer desktop untuk memungkinkan Anda tersambung dari jarak jauh ke baris perintah pada Raspberry Pi.

Microsoft IoT Starter Kit untuk Raspberry Pi 3 atau komponen yang setara. Tutorial ini menggunakan item berikut dari kit:

  • Raspberry Pi 3
  • Kartu MicroSD (dengan NOOBS)
  • Kabel USB Mini
  • Kabel Ethernet

Perangkat lunak desktop yang diperlukan

Anda memerlukan klien SSH di komputer desktop Anda untuk memungkinkan Anda mengakses baris perintah dari jarak jauh pada Raspberry Pi.

  • Windows tidak menyertakan klien SSH. Sebaiknya gunakan PuTTY.
  • Sebagian besar distribusi Linux dan Mac OS menyertakan utilitas SSH baris perintah. Untuk informasi selengkapnya, lihat SSH Menggunakan Linux atau Mac OS.

Perangkat lunak Raspberry Pi yang diperlukan

Artikel ini mengasumsikan Anda telah menginstal versi terbaru OS Raspbian pada Raspberry Pi Anda.

Langkah-langkah berikut menunjukkan kepada Anda cara menyiapkan Raspberry Pi Anda untuk membangun aplikasi C yang terhubung ke akselerator solusi:

  1. Sambungkan ke Raspberry Pi Anda menggunakan ssh. Untuk informasi selengkapnya, lihat SSH (Secure Shell) di situs web Raspberry Pi.

  2. Gunakan perintah berikut untuk memperbarui Raspberry Pi Anda:

    sudo apt-get update
    
  3. Untuk menyelesaikan langkah-langkah dalam panduan ini, ikuti langkah-langkah dalam menyiapkan lingkungan pengembangan Linux Anda untuk menambahkan alat dan pustaka pengembangan yang diperlukan ke Raspberry Pi Anda.

Menampilkan kode

Kode sampel yang digunakan dalam panduan ini tersedia di repositori GitHub Azure IoT C SDK.

Unduh kode sumber dan siapkan proyek

Untuk menyiapkan proyek, kloning atau unduh repositori Azure IoT C SDK dari GitHub.

Sampel terletak di folder sampel/solusi/remote_monitoring_client .

Buka file remote_monitoring.c di folder samples/solutions/remote_monitoring_client di editor teks.

Panduan kode

Bagian ini menjelaskan beberapa bagian kunci dari kode sampel dan menjelaskan hubungannya dengan akselerator solusi Pemantauan Jarak Jauh.

Cuplikan berikut menunjukkan bagaimana properti yang dilaporkan yang menjelaskan kemampuan perangkat ditentukan. Properti ini meliputi:

  • Lokasi perangkat untuk mengaktifkan akselerator solusi untuk menambahkan perangkat ke peta.
  • Versi firmware saat ini.
  • Daftar metode yang didukung perangkat.
  • Skema pesan telemetri yang dikirim oleh perangkat.
typedef struct MESSAGESCHEMA_TAG
{
    char* name;
    char* format;
    char* fields;
} MessageSchema;

typedef struct TELEMETRYSCHEMA_TAG
{
    MessageSchema messageSchema;
} TelemetrySchema;

typedef struct TELEMETRYPROPERTIES_TAG
{
    TelemetrySchema temperatureSchema;
    TelemetrySchema humiditySchema;
    TelemetrySchema pressureSchema;
} TelemetryProperties;

typedef struct CHILLER_TAG
{
    // Reported properties
    char* protocol;
    char* supportedMethods;
    char* type;
    char* firmware;
    FIRMWARE_UPDATE_STATUS firmwareUpdateStatus;
    char* location;
    double latitude;
    double longitude;
    TelemetryProperties telemetry;

    // Manage firmware update process
    char* new_firmware_version;
    char* new_firmware_URI;
} Chiller;

Sampel mencakup fungsi serializeToJson yang membuat serialisasi struktur data ini menggunakan pustaka Parson.

Sampel mencakup beberapa fungsi panggilan balik yang mencetak informasi ke konsol saat klien berinteraksi dengan akselerator solusi:

  • connection_status_callback
  • send_confirm_callback
  • reported_state_callback
  • device_method_callback

Cuplikan berikut menunjukkan fungsi device_method_callback . Fungsi ini menentukan tindakan yang harus diambil ketika panggilan metode diterima dari akselerator solusi. Fungsi ini menerima referensi ke struktur data Chiller dalam parameter userContextCallback . Nilai userContextCallback diatur saat fungsi panggilan balik dikonfigurasi dalam fungsi utama :

static int device_method_callback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* response_size, void* userContextCallback)
{
    Chiller *chiller = (Chiller *)userContextCallback;

    int result;

    (void)printf("Direct method name:    %s\r\n", method_name);

    (void)printf("Direct method payload: %.*s\r\n", (int)size, (const char*)payload);

    if (strcmp("Reboot", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Rebooting\" }")
    }
    else if (strcmp("EmergencyValveRelease", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Releasing emergency valve\" }")
    }
    else if (strcmp("IncreasePressure", method_name) == 0)
    {
        MESSAGERESPONSE(201, "{ \"Response\": \"Increasing pressure\" }")
    }
    else if (strcmp("FirmwareUpdate", method_name) == 0)
    {
        if (chiller->firmwareUpdateStatus != IDLE)
        {
            (void)printf("Attempt to invoke firmware update out of order\r\n");
            MESSAGERESPONSE(400, "{ \"Response\": \"Attempting to initiate a firmware update out of order\" }")
        }
        else
        {
            getFirmwareUpdateValues(chiller, payload);

            if (chiller->new_firmware_version != NULL && chiller->new_firmware_URI != NULL)
            {
                // Create a thread for the long-running firmware update process.
                THREAD_HANDLE thread_apply;
                THREADAPI_RESULT t_result = ThreadAPI_Create(&thread_apply, do_firmware_update, chiller);
                if (t_result == THREADAPI_OK)
                {
                    (void)printf("Starting firmware update thread\r\n");
                    MESSAGERESPONSE(201, "{ \"Response\": \"Starting firmware update thread\" }")
                }
                else
                {
                    (void)printf("Failed to start firmware update thread\r\n");
                    MESSAGERESPONSE(500, "{ \"Response\": \"Failed to start firmware update thread\" }")
                }
            }
            else
            {
                (void)printf("Invalid method payload\r\n");
                MESSAGERESPONSE(400, "{ \"Response\": \"Invalid payload\" }")
            }
        }
    }
    else
    {
        // All other entries are ignored.
        (void)printf("Method not recognized\r\n");
        MESSAGERESPONSE(400, "{ \"Response\": \"Method not recognized\" }")
    }

    return result;
}

Ketika akselerator solusi memanggil metode pembaruan firmware, sampel mendeserialisasi payload JSON dan memulai utas latar belakang untuk menyelesaikan proses pembaruan. Cuplikan berikut menunjukkan do_firmware_update yang berjalan pada utas:

/*
 This is a thread allocated to process a long-running device method call.
 It uses device twin reported properties to communicate status values
 to the Remote Monitoring solution accelerator.
*/
static int do_firmware_update(void *param)
{
    Chiller *chiller = (Chiller *)param;
    printf("Running simulated firmware update: URI: %s, Version: %s\r\n", chiller->new_firmware_URI, chiller->new_firmware_version);

    printf("Simulating download phase...\r\n");
    chiller->firmwareUpdateStatus = DOWNLOADING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    printf("Simulating apply phase...\r\n");
    chiller->firmwareUpdateStatus = APPLYING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    printf("Simulating reboot phase...\r\n");
    chiller->firmwareUpdateStatus = REBOOTING;
    sendChillerReportedProperties(chiller);

    ThreadAPI_Sleep(5000);

    size_t size = strlen(chiller->new_firmware_version) + 1;
    (void)memcpy(chiller->firmware, chiller->new_firmware_version, size);

    chiller->firmwareUpdateStatus = IDLE;
    sendChillerReportedProperties(chiller);

    return 0;
}

Cuplikan berikut menunjukkan bagaimana klien mengirim pesan telemetri ke akselerator solusi. Properti pesan menyertakan skema pesan untuk membantu akselerator solusi menampilkan telemetri di dasbor:

static void send_message(IOTHUB_DEVICE_CLIENT_HANDLE handle, char* message, char* schema)
{
    IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(message);
    if (message_handle != NULL)
    {
        // Set system properties
        (void)IoTHubMessage_SetMessageId(message_handle, "MSG_ID");
        (void)IoTHubMessage_SetCorrelationId(message_handle, "CORE_ID");
        (void)IoTHubMessage_SetContentTypeSystemProperty(message_handle, "application%2fjson");
        (void)IoTHubMessage_SetContentEncodingSystemProperty(message_handle, "utf-8");

        // Set application properties
        MAP_HANDLE propMap = IoTHubMessage_Properties(message_handle);
        (void)Map_AddOrUpdate(propMap, "$$MessageSchema", schema);
        (void)Map_AddOrUpdate(propMap, "$$ContentType", "JSON");

        time_t now = time(0);
        struct tm* timeinfo;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4996) /* Suppress warning about possible unsafe function in Visual Studio */
#endif
        timeinfo = gmtime(&now);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
        char timebuff[50];
        strftime(timebuff, 50, "%Y-%m-%dT%H:%M:%SZ", timeinfo);
        (void)Map_AddOrUpdate(propMap, "$$CreationTimeUtc", timebuff);

        IoTHubDeviceClient_SendEventAsync(handle, message_handle, send_confirm_callback, NULL);

        IoTHubMessage_Destroy(message_handle);
    }
}

Fungsi utama dalam sampel:

  • Menginisialisasi dan mematikan subsistem SDK.
  • Menginisialisasi struktur data Chiller .
  • Mengirim properti yang dilaporkan ke akselerator solusi.
  • Mengonfigurasi fungsi panggilan balik metode perangkat.
  • Mengirim nilai telemetri yang disimulasikan ke akselerator solusi.
int main(void)
{
    srand((unsigned int)time(NULL));
    double minTemperature = 50.0;
    double minPressure = 55.0;
    double minHumidity = 30.0;
    double temperature = 0;
    double pressure = 0;
    double humidity = 0;

    (void)printf("This sample simulates a Chiller device connected to the Remote Monitoring solution accelerator\r\n\r\n");

    // Used to initialize sdk subsystem
    (void)IoTHub_Init();

    (void)printf("Creating IoTHub handle\r\n");
    // Create the iothub handle here
    device_handle = IoTHubDeviceClient_CreateFromConnectionString(connectionString, MQTT_Protocol);
    if (device_handle == NULL)
    {
        (void)printf("Failure creating IotHub device. Hint: Check your connection string.\r\n");
    }
    else
    {
        // Setting connection status callback to get indication of connection to iothub
        (void)IoTHubDeviceClient_SetConnectionStatusCallback(device_handle, connection_status_callback, NULL);

        Chiller chiller;
        memset(&chiller, 0, sizeof(Chiller));
        chiller.protocol = "MQTT";
        chiller.supportedMethods = "Reboot,FirmwareUpdate,EmergencyValveRelease,IncreasePressure";
        chiller.type = "Chiller";
        size_t size = strlen(initialFirmwareVersion) + 1;
        chiller.firmware = malloc(size);
        if (chiller.firmware == NULL)
        {
            (void)printf("Chiller Firmware failed to allocate memory.\r\n");
        }
        else
        {
            memcpy(chiller.firmware, initialFirmwareVersion, size);
            chiller.firmwareUpdateStatus = IDLE;
            chiller.location = "Building 44";
            chiller.latitude = 47.638928;
            chiller.longitude = -122.13476;
            chiller.telemetry.temperatureSchema.messageSchema.name = "chiller-temperature;v1";
            chiller.telemetry.temperatureSchema.messageSchema.format = "JSON";
            chiller.telemetry.temperatureSchema.messageSchema.fields = "{\"temperature\":\"Double\",\"temperature_unit\":\"Text\"}";
            chiller.telemetry.humiditySchema.messageSchema.name = "chiller-humidity;v1";
            chiller.telemetry.humiditySchema.messageSchema.format = "JSON";
            chiller.telemetry.humiditySchema.messageSchema.fields = "{\"humidity\":\"Double\",\"humidity_unit\":\"Text\"}";
            chiller.telemetry.pressureSchema.messageSchema.name = "chiller-pressure;v1";
            chiller.telemetry.pressureSchema.messageSchema.format = "JSON";
            chiller.telemetry.pressureSchema.messageSchema.fields = "{\"pressure\":\"Double\",\"pressure_unit\":\"Text\"}";

            sendChillerReportedProperties(&chiller);

            (void)IoTHubDeviceClient_SetDeviceMethodCallback(device_handle, device_method_callback, &chiller);

            while (1)
            {
                temperature = minTemperature + ((double)(rand() % 10) + 5);
                pressure = minPressure + ((double)(rand() % 10) + 5);
                humidity = minHumidity + ((double)(rand() % 20) + 5);

                if (chiller.firmwareUpdateStatus == IDLE)
                {
                    (void)printf("Sending sensor value Temperature = %f %s,\r\n", temperature, "F");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"temperature\":%.2f,\"temperature_unit\":\"F\"}", temperature);
                    send_message(device_handle, msgText, chiller.telemetry.temperatureSchema.messageSchema.name);


                    (void)printf("Sending sensor value Pressure = %f %s,\r\n", pressure, "psig");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"pressure\":%.2f,\"pressure_unit\":\"psig\"}", pressure);
                    send_message(device_handle, msgText, chiller.telemetry.pressureSchema.messageSchema.name);


                    (void)printf("Sending sensor value Humidity = %f %s,\r\n", humidity, "%");
                    (void)sprintf_s(msgText, sizeof(msgText), "{\"humidity\":%.2f,\"humidity_unit\":\"%%\"}", humidity);
                    send_message(device_handle, msgText, chiller.telemetry.humiditySchema.messageSchema.name);
                }

                ThreadAPI_Sleep(5000);
            }

            (void)printf("\r\nShutting down\r\n");

            // Clean up the iothub sdk handle and free resources
            IoTHubDeviceClient_Destroy(device_handle);
            free(chiller.firmware);
            free(chiller.new_firmware_URI);
            free(chiller.new_firmware_version);
        }
    }
    // Shutdown the sdk subsystem
    IoTHub_Deinit();

    return 0;
}

Membangun dan menjalankan aplikasi

Langkah-langkah berikut menjelaskan cara menggunakan CMake untuk membangun aplikasi klien. Aplikasi klien pemantauan jarak jauh dibangun sebagai bagian dari proses build untuk SDK.

  1. Edit file remote_monitoring.c untuk mengganti <connectionstring> dengan string koneksi perangkat yang Anda catat di awal panduan cara ini saat Anda menambahkan perangkat ke akselerator solusi.

  2. Navigasi ke akar salinan kloning repositori SDK Azure IoT C Anda dan jalankan perintah berikut untuk membangun aplikasi klien:

    mkdir cmake
    cd cmake
    cmake ../
    make
    
  3. Jalankan aplikasi klien dan kirim telemetri ke IoT Hub:

    ./samples/solutions/remote_monitoring_client/remote_monitoring_client
    

    Konsol menampilkan pesan sebagai:

    • Aplikasi mengirimkan telemetri sampel ke akselerator solusi.
    • Merespons metode yang dipanggil dari dasbor solusi.

Lihat telemetri perangkat

Anda dapat melihat telemetri yang dikirim dari perangkat Anda di halaman Penjelajah Perangkat dalam solusi.

  1. Pilih perangkat yang Anda provisikan dalam daftar perangkat di halaman Penjelajah Perangkat . Panel menampilkan informasi tentang perangkat Anda termasuk plot telemetri perangkat:

    Lihat detail perangkat

  2. Pilih Tekanan untuk mengubah tampilan telemetri:

    Lihat telemetri tekanan

  3. Untuk melihat informasi diagnostik tentang perangkat Anda, gulir ke bawah ke Diagnostik:

    Melihat diagnostik perangkat

Bertindak di perangkat Anda

Untuk memanggil metode di perangkat Anda, gunakan halaman Penjelajah Perangkat di solusi Pemantauan Jarak Jauh. Misalnya, dalam solusi Pemantauan Jarak Jauh Perangkat Chiller menerapkan metode Reboot .

  1. Pilih Perangkat untuk menavigasi ke halaman Penjelajah Perangkat dalam solusi.

  2. Pilih perangkat yang Anda provisikan dalam daftar perangkat di halaman Penjelajah Perangkat :

    Pilih perangkat nyata Anda

  3. Untuk menampilkan daftar metode yang dapat Anda panggil di perangkat Anda, pilih Pekerjaan, lalu Metode. Untuk menjadwalkan pekerjaan yang akan dijalankan di beberapa perangkat, Anda dapat memilih beberapa perangkat dalam daftar. Panel Pekerjaan menunjukkan jenis metode yang umum untuk semua perangkat yang Anda pilih.

  4. Pilih Reboot, atur nama pekerjaan ke RebootPhysicalChiller lalu pilih Terapkan:

    Menjadwalkan pembaruan firmware

  5. Urutan pesan ditampilkan di konsol yang menjalankan kode perangkat Anda saat perangkat yang disimulasikan menangani metode .

Catatan

Untuk melacak status pekerjaan dalam solusi, pilih Tampilkan Status Pekerjaan.

Langkah berikutnya

Artikel Menyesuaikan akselerator solusi Pemantauan Jarak Jauh menjelaskan beberapa cara untuk menyesuaikan akselerator solusi.