13 KiB
IoT 디바이스를 클라우드에 연결하기 - Wio Terminal
이 강의의 이번 단계에서는 Wio Terminal을 IoT Hub에 연결하여 텔레메트리를 전송하고 명령을 수신하는 방법을 배웁니다.
디바이스를 IoT Hub에 연결하기
다음 단계는 디바이스를 IoT Hub에 연결하는 것입니다.
작업 - IoT Hub에 연결하기
-
VS Code에서
soil-moisture-sensor
프로젝트를 엽니다. -
platformio.ini
파일을 열고,knolleary/PubSubClient
라이브러리 의존성을 제거합니다. 이 라이브러리는 공용 MQTT 브로커에 연결하기 위해 사용되었으며, IoT Hub에 연결하는 데는 필요하지 않습니다. -
다음 라이브러리 의존성을 추가합니다:
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에 연결할 수 있도록 합니다. -
platformio.ini
파일 하단에 다음을 추가합니다:build_flags = -DDONT_USE_UPLOADTOBLOB
이는 Arduino IoT Hub 코드를 컴파일할 때 필요한 컴파일러 플래그를 설정합니다.
-
config.h
헤더 파일을 열고, 모든 MQTT 설정을 제거한 뒤 디바이스 연결 문자열에 대한 다음 상수를 추가합니다:// IoT Hub settings const char *CONNECTION_STRING = "<connection string>";
<connection string>
을 이전에 복사한 디바이스의 연결 문자열로 대체하세요. -
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); }
이 코드는 강의 범위를 벗어납니다. NTP 서버에서 현재 시간을 가져와 Wio Terminal의 시계를 설정하는
initTime
이라는 함수를 정의합니다. -
main.cpp
파일을 열고,PubSubClient.h
헤더 파일,PubSubClient
변수 선언,reconnectMQTTClient
및createMQTTClient
메서드, 그리고 이 변수와 메서드에 대한 모든 호출을 포함한 모든 MQTT 코드를 제거합니다. 이 파일에는 WiFi에 연결하고, 토양 습도를 가져오고, 이를 JSON 문서로 생성하는 코드만 포함되어야 합니다. -
IoT Hub 라이브러리와 시간을 설정하기 위한 헤더 파일을 포함하려면,
main.cpp
파일 상단에 다음#include
지시문을 추가하세요:#include <AzureIoTHub.h> #include <AzureIoTProtocol_MQTT.h> #include <iothubtransportmqtt.h> #include "ntp.h"
-
현재 시간을 설정하려면
setup
함수 끝에 다음 호출을 추가하세요:initTime();
-
파일 상단의 include 지시문 바로 아래에 다음 변수 선언을 추가하세요:
IOTHUB_DEVICE_CLIENT_LL_HANDLE _device_ll_handle;
이는 IoT Hub에 대한 연결 핸들인
IOTHUB_DEVICE_CLIENT_LL_HANDLE
을 선언합니다. -
그 아래에 다음 코드를 추가하세요:
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에 연결 상태가 변경될 때 호출되는 콜백 함수를 선언합니다. 연결 상태는 시리얼 포트로 전송됩니다.
-
그 아래에 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를 기반으로 합니다. 연결이 실패하면 시리얼 포트로 전송됩니다. 출력에서 이를 확인하면 연결 문자열을 확인하세요. 마지막으로 연결 상태 콜백이 설정됩니다. -
이 함수를
setup
함수에서initTime
호출 아래에 추가하세요:connectIoTHub();
-
MQTT 클라이언트와 마찬가지로, 이 코드는 단일 스레드에서 실행되므로 허브에서 보내고 허브로 보내는 메시지를 처리할 시간이 필요합니다. 이를 위해
loop
함수 상단에 다음을 추가하세요:IoTHubDeviceClient_LL_DoWork(_device_ll_handle);
-
코드를 빌드하고 업로드하세요. 시리얼 모니터에서 연결을 확인할 수 있습니다:
Connecting to WiFi.. Connected! Fetched NTP epoch time is: 1619983687 Sending telemetry {"soil_moisture":391} The device client is connected to iothub
출력에서 NTP 시간이 가져온 후 디바이스 클라이언트가 연결되는 것을 볼 수 있습니다. 연결에는 몇 초가 걸릴 수 있으므로, 디바이스가 연결되는 동안 출력에서 토양 습도를 볼 수 있습니다.
💁 NTP의 UNIX 시간을 unixtimestamp.com과 같은 웹사이트를 사용하여 더 읽기 쉬운 형식으로 변환할 수 있습니다.
텔레메트리 전송
이제 디바이스가 연결되었으므로, MQTT 브로커 대신 IoT Hub로 텔레메트리를 전송할 수 있습니다.
작업 - 텔레메트리 전송
-
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 메시지를 생성하고, 이를 허브로 전송한 뒤 메시지 객체를 정리합니다.
-
이 코드를
loop
함수에서 텔레메트리를 시리얼 포트로 전송하는 줄 바로 뒤에 호출하세요:sendTelemetry(telemetry.c_str());
명령 처리
디바이스는 서버 코드에서 릴레이를 제어하기 위한 명령을 처리해야 합니다. 이는 직접 메서드 요청으로 전송됩니다.
작업 - 직접 메서드 요청 처리
-
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
매개변수에서 사용할 수 있습니다. -
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
를 반환합니다. -
다음 코드를
connectIoTHub
함수 끝에 추가하여 콜백을 연결하세요:IoTHubClient_LL_SetDeviceMethodCallback(_device_ll_handle, directMethodCallback, NULL);
-
loop
함수는 IoT Hub에서 전송한 이벤트를 처리하기 위해IoTHubDeviceClient_LL_DoWork
함수를 호출합니다. 이는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
를 호출하고 100ms 동안 지연합니다. 이는 주어진delay_time
매개변수만큼 지연하기 위해 필요한 만큼 반복됩니다. 이로 인해 디바이스는 직접 메서드 요청을 처리하기 위해 최대 100ms만 대기합니다. -
loop
함수에서IoTHubDeviceClient_LL_DoWork
호출을 제거하고,delay(10000)
호출을 다음 코드로 대체하세요:work_delay(10000);
💁 이 코드는 code/wio-terminal 폴더에서 확인할 수 있습니다.
😀 이제 토양 습도 센서 프로그램이 IoT Hub에 연결되었습니다!
면책 조항:
이 문서는 AI 번역 서비스 Co-op Translator를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있지만, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다.