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/tw/2-farm/lessons/4-migrate-your-plant-to-the.../wio-terminal-connect-hub.md

304 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "28320305a35ea3bc59c41fe146a2e6ed",
"translation_date": "2025-08-24T22:49:57+00:00",
"source_file": "2-farm/lessons/4-migrate-your-plant-to-the-cloud/wio-terminal-connect-hub.md",
"language_code": "tw"
}
-->
# 將您的 IoT 裝置連接到雲端 - Wio Terminal
在本課程的這部分,您將把 Wio Terminal 連接到 IoT Hub以傳送遙測數據並接收指令。
## 將裝置連接到 IoT Hub
下一步是將您的裝置連接到 IoT Hub。
### 任務 - 連接到 IoT Hub
1. 在 VS Code 中打開 `soil-moisture-sensor` 專案。
1. 打開 `platformio.ini` 檔案。移除 `knolleary/PubSubClient` 函式庫的依賴項。這個函式庫是用來連接到公共 MQTT broker 的,但連接到 IoT Hub 時不需要。
1. 添加以下函式庫依賴項:
```ini
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。
1.`platformio.ini` 檔案的底部添加以下內容:
```ini
build_flags =
-DDONT_USE_UPLOADTOBLOB
```
這會設置一個編譯器標誌,在編譯 Arduino IoT Hub 程式碼時需要用到。
1. 打開 `config.h` 標頭檔案。移除所有 MQTT 設定,並添加以下裝置連接字串的常數:
```cpp
// IoT Hub settings
const char *CONNECTION_STRING = "<connection string>";
```
`<connection string>` 替換為您之前複製的裝置連接字串。
1. 連接到 IoT Hub 使用的是基於時間的權杖。這意味著 IoT 裝置需要知道當前時間。與 Windows、macOS 或 Linux 等操作系統不同,微控制器不會自動通過網際網路同步當前時間。因此,您需要添加程式碼從 [NTP](https://wikipedia.org/wiki/Network_Time_Protocol) 伺服器獲取當前時間。一旦獲取時間,它可以存儲在 Wio Terminal 的實時時鐘中,允許在稍後請求正確時間,前提是裝置未斷電。新增一個名為 `ntp.h` 的檔案,並添加以下程式碼:
```cpp
#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 的時鐘。
1. 打開 `main.cpp` 檔案,移除所有 MQTT 程式碼,包括 `PubSubClient.h` 標頭檔案、`PubSubClient` 變數的宣告、`reconnectMQTTClient` 和 `createMQTTClient` 方法,以及對這些變數和方法的任何調用。該檔案應僅包含連接 WiFi、獲取土壤濕度並用其創建 JSON 文件的程式碼。
1.`main.cpp` 檔案的頂部添加以下 `#include` 指令,以包含 IoT Hub 函式庫和設置時間的標頭檔案:
```cpp
#include <AzureIoTHub.h>
#include <AzureIoTProtocol_MQTT.h>
#include <iothubtransportmqtt.h>
#include "ntp.h"
```
1.`setup` 函數的末尾添加以下調用來設置當前時間:
```cpp
initTime();
```
1. 在檔案頂部的 include 指令下方添加以下變數宣告:
```cpp
IOTHUB_DEVICE_CLIENT_LL_HANDLE _device_ll_handle;
```
這宣告了一個 `IOTHUB_DEVICE_CLIENT_LL_HANDLE`,即 IoT Hub 連接的句柄。
1. 在這之下添加以下程式碼:
```cpp
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 的狀態發生變化(例如連接或斷開)時會被調用。狀態會被發送到序列埠。
1. 在這之下添加一個函數來連接到 IoT Hub
```cpp
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。如果連接失敗錯誤會被發送到序列埠——如果您在輸出中看到這個錯誤請檢查連接字串。最後設置連接狀態回調。
1.`setup` 函數中,將此函數調用添加到 `initTime` 調用的下方:
```cpp
connectIoTHub();
```
1. 與 MQTT 客戶端一樣,這段程式碼在單一執行緒上運行,因此需要時間來處理由 Hub 發送和接收的消息。在 `loop` 函數的頂部添加以下內容來實現:
```cpp
IoTHubDeviceClient_LL_DoWork(_device_ll_handle);
```
1. 編譯並上傳這段程式碼。您會在序列監視器中看到連接:
```output
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](https://www.unixtimestamp.com) 這樣的網站將 UNIX 時間轉換為更易讀的格式。
## 傳送遙測數據
現在您的裝置已連接,您可以將遙測數據發送到 IoT Hub而不是 MQTT broker。
### 任務 - 傳送遙測數據
1.`setup` 函數上方添加以下函數:
```cpp
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然後清理消息物件。
1.`loop` 函數中,在將遙測數據發送到序列埠的那一行之後調用這段程式碼:
```cpp
sendTelemetry(telemetry.c_str());
```
## 處理指令
您的裝置需要處理來自伺服器程式碼的指令,以控制繼電器。這是作為直接方法請求發送的。
## 任務 - 處理直接方法請求
1.`connectIoTHub` 函數之前添加以下程式碼:
```cpp
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` 參數獲取。
1.`directMethodCallback` 函數的末尾添加以下程式碼:
```cpp
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 文件作為結果:
```JSON
{
"Result": ""
}
```
然後將其複製到 `response` 參數中,並在 `response_size` 參數中設置該回應的大小。最後,這段程式碼返回 `IOTHUB_CLIENT_OK`,表示方法已正確處理。
1.`connectIoTHub` 函數的末尾添加以下程式碼來連接回調:
```cpp
IoTHubClient_LL_SetDeviceMethodCallback(_device_ll_handle, directMethodCallback, NULL);
```
1. `loop` 函數會調用 `IoTHubDeviceClient_LL_DoWork` 函數來處理 IoT Hub 發送的事件。由於 `delay` 的原因,這僅每 10 秒調用一次,這意味著直接方法僅每 10 秒處理一次。為了提高效率,可以將 10 秒的延遲分解為多個較短的延遲,每次調用 `IoTHubDeviceClient_LL_DoWork`。為此,在 `loop` 函數之前添加以下程式碼:
```cpp
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 毫秒來處理直接方法請求。
1. `loop` 函數中,移除對 `IoTHubDeviceClient_LL_DoWork` 的調用,並將 `delay(10000)` 替換為以下程式碼來調用新函數:
```cpp
work_delay(10000);
```
> 💁 您可以在 [code/wio-terminal](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/wio-terminal) 資料夾中找到這段程式碼。
😀 您的土壤濕度感測器程式已成功連接到 IoT Hub
**免責聲明**
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對於因使用本翻譯而產生的任何誤解或錯誤解讀概不負責。