You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
IoT-For-Beginners/translations/mo/2-farm/lessons/4-migrate-your-plant-to-the.../wio-terminal-connect-hub.md

11 KiB

將您的 IoT 裝置連接到雲端 - Wio Terminal

在本課程的這部分,您將把 Wio Terminal 連接到 IoT Hub以傳送遙測數據並接收指令。

將您的裝置連接到 IoT Hub

下一步是將您的裝置連接到 IoT Hub。

任務 - 連接到 IoT Hub

  1. 在 VS Code 中打開 soil-moisture-sensor 專案。

  2. 打開 platformio.ini 文件。移除 knolleary/PubSubClient 庫的依賴項。這個庫是用來連接到公共 MQTT broker 的,但連接到 IoT Hub 不需要它。

  3. 添加以下庫依賴項:

    seeed-studio/Seeed Arduino RTC @ 2.0.0
    arduino-libraries/AzureIoTHub @ 1.6.0
    azure/AzureIoTUtility @ 1.6.1
    azure/AzureIoTProtocol_MQTT @ 1.6.0
    azure/AzureIoTProtocol_HTTP @ 1.6.0
    azure/AzureIoTSocket_WiFi @ 1.0.2
    

    Seeed Arduino RTC 庫提供了與 Wio Terminal 的實時時鐘交互的代碼,用於追蹤時間。其餘的庫則允許您的 IoT 裝置連接到 IoT Hub。

  4. platformio.ini 文件的底部添加以下內容:

    build_flags =
        -DDONT_USE_UPLOADTOBLOB
    

    這設置了一個編譯器標誌,在編譯 Arduino IoT Hub 代碼時需要。

  5. 打開 config.h 標頭文件。移除所有 MQTT 設定,並添加以下裝置連接字串的常量:

    // IoT Hub settings
    const char *CONNECTION_STRING = "<connection string>";
    

    <connection string> 替換為您之前複製的裝置連接字串。

  6. 連接到 IoT Hub 使用基於時間的令牌。這意味著 IoT 裝置需要知道當前時間。與 Windows、macOS 或 Linux 等操作系統不同,微控制器不會自動通過網路同步當前時間。因此,您需要添加代碼以從 NTP 伺服器獲取當前時間。一旦獲取了時間,它可以存儲在 Wio Terminal 的實時時鐘中,允許在稍後日期請求正確的時間,假設裝置未斷電。新增一個名為 ntp.h 的文件,並添加以下代碼:

    #pragma once
    
    #include "DateTime.h"
    #include <time.h>
    #include "samd/NTPClientAz.h"
    #include <sys/time.h>
    
    static void initTime()
    {
        WiFiUDP _udp;
        time_t epochTime = (time_t)-1;
        NTPClientAz ntpClient;
    
        ntpClient.begin();
    
        while (true)
        {
            epochTime = ntpClient.getEpochTime("0.pool.ntp.org");
    
            if (epochTime == (time_t)-1)
            {
                Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry.");
                delay(2000);
            }
            else
            {
                Serial.print("Fetched NTP epoch time is: ");
    
                char buff[32];
                sprintf(buff, "%.f", difftime(epochTime, (time_t)0));
                Serial.println(buff);
                break;
            }
        }
    
        ntpClient.end();
    
        struct timeval tv;
        tv.tv_sec = epochTime;
        tv.tv_usec = 0;
    
        settimeofday(&tv, NULL);
    }
    

    此代碼的細節超出了本課程的範圍。它定義了一個名為 initTime 的函數,用於從 NTP 伺服器獲取當前時間並用它設置 Wio Terminal 的時鐘。

  7. 打開 main.cpp 文件,移除所有 MQTT 代碼,包括 PubSubClient.h 標頭文件、PubSubClient 變數的聲明、reconnectMQTTClientcreateMQTTClient 方法,以及對這些變數和方法的任何調用。此文件應僅包含連接 WiFi、獲取土壤濕度並創建 JSON 文檔的代碼。

  8. main.cpp 文件的頂部添加以下 #include 指令,以包含 IoT Hub 庫和設置時間的標頭文件:

    #include <AzureIoTHub.h>
    #include <AzureIoTProtocol_MQTT.h>
    #include <iothubtransportmqtt.h>
    #include "ntp.h"
    
  9. setup 函數的末尾添加以下調用以設置當前時間:

    initTime();
    
  10. 在文件的頂部,包含指令下方添加以下變數聲明:

    IOTHUB_DEVICE_CLIENT_LL_HANDLE _device_ll_handle;
    

    這聲明了一個 IOTHUB_DEVICE_CLIENT_LL_HANDLE,用於連接到 IoT Hub 的句柄。

  11. 在此下方添加以下代碼:

    static void connectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void *user_context)
    {
        if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED)
        {
            Serial.println("The device client is connected to iothub");
        }
        else
        {
            Serial.println("The device client has been disconnected");
        }
    }
    

    這聲明了一個回調函數,當連接到 IoT Hub 的狀態改變(例如連接或斷開)時會被調用。狀態會被發送到串口。

  12. 在此下方添加一個函數以連接到 IoT Hub

    void connectIoTHub()
    {
        IoTHub_Init();
    
        _device_ll_handle = IoTHubDeviceClient_LL_CreateFromConnectionString(CONNECTION_STRING, MQTT_Protocol);
    
        if (_device_ll_handle == NULL)
        {
            Serial.println("Failure creating Iothub device. Hint: Check your connection string.");
            return;
        }
    
        IoTHubDeviceClient_LL_SetConnectionStatusCallback(_device_ll_handle, connectionStatusCallback, NULL);
    }
    

    此代碼初始化 IoT Hub 庫代碼,然後使用 config.h 標頭文件中的連接字串創建連接。此連接基於 MQTT。如果連接失敗會將此信息發送到串口——如果您在輸出中看到此信息請檢查連接字串。最後設置了連接狀態回調。

  13. setup 函數中,將此函數調用添加到 initTime 調用的下方:

    connectIoTHub();
    
  14. 與 MQTT 客戶端一樣,此代碼在單一線程上運行,因此需要時間來處理由 Hub 發送和發送到 Hub 的消息。在 loop 函數的頂部添加以下內容以完成此操作:

    IoTHubDeviceClient_LL_DoWork(_device_ll_handle);
    
  15. 編譯並上傳此代碼。您將在串口監視器中看到連接:

    Connecting to WiFi..
    Connected!
    Fetched NTP epoch time is: 1619983687
    Sending telemetry {"soil_moisture":391}
    The device client is connected to iothub
    

    在輸出中,您可以看到 NTP 時間被獲取,接著是裝置客戶端連接。連接可能需要幾秒鐘,因此在裝置連接時,您可能會在輸出中看到土壤濕度。

    💁 您可以使用像 unixtimestamp.com 這樣的網站將 NTP 的 UNIX 時間轉換為更易讀的版本。

傳送遙測數據

現在您的裝置已連接,您可以將遙測數據傳送到 IoT Hub而不是 MQTT broker。

任務 - 傳送遙測數據

  1. setup 函數上方添加以下函數:

    void sendTelemetry(const char *telemetry)
    {
        IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(telemetry);
        IoTHubDeviceClient_LL_SendEventAsync(_device_ll_handle, message_handle, NULL, NULL);
        IoTHubMessage_Destroy(message_handle);
    }
    

    此代碼從作為參數傳遞的字串創建 IoT Hub 消息,將其發送到 Hub然後清理消息對象。

  2. loop 函數中,將此代碼調用添加到遙測數據發送到串口的行之後:

    sendTelemetry(telemetry.c_str());
    

處理指令

您的裝置需要處理來自伺服器代碼的指令,以控制繼電器。這是通過直接方法請求發送的。

任務 - 處理直接方法請求

  1. connectIoTHub 函數之前添加以下代碼:

    int directMethodCallback(const char *method_name, const unsigned char *payload, size_t size, unsigned char **response, size_t *response_size, void *userContextCallback)
    {
        Serial.printf("Direct method received %s\r\n", method_name);
    
        if (strcmp(method_name, "relay_on") == 0)
        {
            digitalWrite(PIN_WIRE_SCL, HIGH);
        }
        else if (strcmp(method_name, "relay_off") == 0)
        {
            digitalWrite(PIN_WIRE_SCL, LOW);
        }
    }
    

    此代碼定義了一個回調方法,當 IoT Hub 庫接收到直接方法請求時可以調用。請求的方法通過 method_name 參數傳遞。此函數將調用的方法打印到串口,然後根據方法名稱打開或關閉繼電器。

    💁 這也可以通過單一直接方法請求實現,將繼電器的期望狀態作為有效負載傳遞,該有效負載可以通過 payload 參數獲得。

  2. directMethodCallback 函數的末尾添加以下代碼:

    char resultBuff[16];
    sprintf(resultBuff, "{\"Result\":\"\"}");
    *response_size = strlen(resultBuff);
    *response = (unsigned char *)malloc(*response_size);
    memcpy(*response, resultBuff, *response_size);
    
    return IOTHUB_CLIENT_OK;
    

    直接方法請求需要響應,響應分為兩部分——作為文本的響應和返回代碼。此代碼將創建以下 JSON 文檔作為結果:

    {
        "Result": ""
    }
    

    然後將其複製到 response 參數中,並在 response_size 參數中設置此響應的大小。此代碼然後返回 IOTHUB_CLIENT_OK,表示方法已正確處理。

  3. connectIoTHub 函數的末尾添加以下代碼以連接回調:

    IoTHubClient_LL_SetDeviceMethodCallback(_device_ll_handle, directMethodCallback, NULL);
    
  4. loop 函數將調用 IoTHubDeviceClient_LL_DoWork 函數以處理 IoT Hub 發送的事件。由於 delay 的原因,此函數僅每 10 秒調用一次,這意味著直接方法僅每 10 秒處理一次。為了提高效率10 秒的延遲可以分為多個較短的延遲,每次都調用 IoTHubDeviceClient_LL_DoWork。為此,在 loop 函數上方添加以下代碼:

    void work_delay(int delay_time)
    {
        int current = 0;
        do
        {
            IoTHubDeviceClient_LL_DoWork(_device_ll_handle);
            delay(100);
            current += 100;
        } while (current < delay_time);
    }
    

    此代碼將重複循環,調用 IoTHubDeviceClient_LL_DoWork 並每次延遲 100 毫秒。它將根據 delay_time 參數的值執行多次,這意味著裝置最多等待 100 毫秒來處理直接方法請求。

  5. loop 函數中,移除對 IoTHubDeviceClient_LL_DoWork 的調用,並將 delay(10000) 替換為以下代碼以調用此新函數:

    work_delay(10000);
    

💁 您可以在 code/wio-terminal 文件夾中找到此代碼。

😀 您的土壤濕度感測器程式已成功連接到 IoT Hub


免責聲明
本文件已使用 AI 翻譯服務 Co-op Translator 進行翻譯。儘管我們努力確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵信息,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。