다음을 통해 공유


자습서 - 디바이스 SDK를 사용하지 않고 MQTT를 사용하여 IoT 디바이스 클라이언트 개발

가능한 경우 Azure IoT 디바이스 SDK 중 하나를 사용하여 IoT 디바이스 클라이언트를 빌드해야 합니다. 그러나 메모리가 제한된 디바이스를 사용하는 시나리오에서는 MQTT 라이브러리를 사용하여 IoT 허브와 통신해야 할 수도 있습니다.

이 자습서의 샘플은 Eclipse Mosquitto MQTT 라이브러리를 사용합니다.

이 자습서에서는 다음 방법을 알아봅니다.

  • C 언어 디바이스 클라이언트 샘플 애플리케이션을 빌드합니다.
  • MQTT 라이브러리를 사용하여 원격 분석을 보내는 샘플을 실행합니다.
  • MQTT 라이브러리를 사용하는 샘플을 실행하여 IoT Hub에서 보낸 클라우드-디바이스 메시지를 처리합니다.
  • MQTT 라이브러리를 사용하는 샘플을 실행하여 디바이스에서 디바이스 쌍을 관리합니다.

Windows 또는 Linux 개발 컴퓨터를 사용하여 이 자습서의 단계를 완료할 수 있습니다.

Azure 구독이 없는 경우, 시작하기 전에 무료 계정을 만드십시오.

필수 조건

개발 컴퓨터 필수 조건

  1. Visual Studio(커뮤니티, 전문가 또는 기업)를 설치합니다. C++를 사용한 데스크톱 개발 워크로드를 사용하도록 설정해야 합니다.

  2. CMake를 설치합니다. 모든 사용자의 시스템 PATH에 CMake 추가 옵션을 사용하도록 설정합니다.

  3. Mosquittox64 버전을 설치합니다.

Azure CLI에 대한 환경 준비

환경 설정

IoT Hub가 아직 없는 경우 다음 명령을 실행하여 mqtt-sample-rg라는 리소스 그룹에 무료 계층 IoT Hub를 만듭니다. 이 명령은 만들 IoT 허브 이름의 예로 my-hub 이름을 사용합니다. my-hub 대신 사용할 고유한 IoT 허브 이름을 선택합니다.

az group create --name mqtt-sample-rg --location eastus
az iot hub create --name my-hub --resource-group mqtt-sample-rg --sku F1 

나중에 필요하므로 IoT Hub의 이름을 기록해 둡니다.

IoT 허브에 디바이스 등록 다음 명령은 my-hub라는 IoT Hub에 mqtt-dev-01이라는 디바이스를 등록합니다. IoT Hub의 이름을 사용해야 합니다.

az iot hub device-identity create --hub-name my-hub --device-id mqtt-dev-01

다음 명령을 사용하여 IoT 허브에 대한 디바이스 액세스 권한을 부여하는 SAS 토큰을 만듭니다. IoT Hub의 이름을 사용해야 합니다.

az iot hub generate-sas-token --device-id mqtt-dev-01 --hub-name my-hub --du 7200

나중에 필요할 때 명령이 출력하는 SAS 토큰을 기록해 둡니다. SAS 토큰은 SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761과 같습니다.

팁 (조언)

기본적으로 SAS 토큰은 60분 동안 유효합니다. 이전 명령의 --du 7200 옵션은 토큰 기간을 2시간으로 확장합니다. 사용할 준비가 되기 전에 만료되면 새로 생성합니다. 기간이 더 긴 토큰을 만들 수도 있습니다. 자세한 내용은 az iot hub generate-sas-token을 참조하세요.

중요합니다

이 문서에서는 공유 액세스 서명(대칭 키 인증이라고도 함)을 사용하여 디바이스를 연결하는 단계를 설명합니다. 이 인증 방법은 테스트와 평가에 편리하지만, X.509 인증서를 사용하여 디바이스를 인증하는 것이 더 안전한 방식입니다. 자세한 내용은 보안 모범 사례 > 연결 보안을 참조하세요.

샘플 리포지토리 복제

다음 명령을 실행하여 로컬 머신의 적절한 위치에 샘플 리포지토리를 복제합니다.

git clone https://github.com/Azure-Samples/IoTMQTTSample.git

리포지토리에는 다음도 포함됩니다.

  • paho-mqtt 라이브러리를 사용하는 Python 샘플입니다.
  • mosquitto_pub CLI를 사용하여 IoT 허브와 상호 작용하는 방법에 대한 지침입니다.

C 샘플 빌드

샘플을 빌드하기 전에 IoT Hub 및 디바이스 세부 정보를 추가해야 합니다. 복제된 IoTMQTTSample 리포지토리에서 mosquitto/src/config.h 파일을 엽니다. 다음과 같이 IoT 허브 이름, 디바이스 ID 및 SAS 토큰을 추가합니다. IoT Hub의 이름을 사용해야 합니다.

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#define IOTHUBNAME "my-hub"
#define DEVICEID   "mqtt-dev-01"
#define SAS_TOKEN  "SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761"

#define CERTIFICATEFILE CERT_PATH "IoTHubRootCA.crt.pem"

비고

IoTHubRootCA.crt.pem 파일에는 TLS 연결을 위한 CA 루트 인증서가 포함되어 있습니다.

변경 내용을 mosquitto/src/config.h 파일에 저장합니다.

샘플을 빌드하려면 셸에서 다음 명령을 실행합니다.

cd mosquitto
cmake -Bbuild
cmake --build build

Linux에서 이진 파일은 mosquitto 폴더 아래의 ./build 폴더에 있습니다.

Windows에서 이진 파일은 mosquitto 폴더 아래의 .\build\Debug 폴더에 있습니다.

원격 분석 전송

mosquitto_telemetry 샘플은 MQTT 라이브러리를 사용하여 IoT Hub에 디바이스-클라우드 원격 분석 메시지를 보내는 방법을 보여 줍니다.

샘플 애플리케이션을 실행하기 전에 다음 명령을 실행하여 IoT Hub에 대한 이벤트 모니터를 시작합니다. IoT Hub의 이름을 사용해야 합니다.

az iot hub monitor-events --hub-name my-hub

mosquitto_telemetry 샘플을 실행합니다. 예를 들어, Linux에서:

./build/mosquitto_telemetry

az iot hub monitor-events는 디바이스에서 보낸 페이로드를 보여 주는 다음 출력을 생성합니다.

Starting event monitor, use ctrl-c to stop...
{
    "event": {
        "origin": "mqtt-dev-01",
        "module": "",
        "interface": "",
        "component": "",
        "payload": "Bonjour MQTT from Mosquitto"
    }
}

이제 이벤트 모니터를 중지할 수 있습니다.

코드 검토

다음 코드 조각은 mosquitto/src/mosquitto_telemetry.cpp 파일에서 가져온 것입니다.

다음 문은 연결 정보와 원격 분석 메시지를 보내는 데 사용하는 MQTT 항목의 이름을 정의합니다.

#define HOST IOTHUBNAME ".azure-devices.net"
#define PORT 8883
#define USERNAME HOST "/" DEVICEID "/?api-version=2020-09-30"

#define TOPIC "devices/" DEVICEID "/messages/events/"

main 함수는 IoT Hub 인증을 위한 사용자 이름과 암호를 설정합니다. 암호는 디바이스용으로 만든 SAS 토큰입니다.

mosquitto_username_pw_set(mosq, USERNAME, SAS_TOKEN);

이 샘플은 MQTT 항목을 사용하여 IoT 허브에 원격 분석 메시지를 보냅니다.

int msgId  = 42;
char msg[] = "Bonjour MQTT from Mosquitto";

// once connected, we can publish a Telemetry message
printf("Publishing....\r\n");
rc = mosquitto_publish(mosq, &msgId, TOPIC, sizeof(msg) - 1, msg, 1, true);
if (rc != MOSQ_ERR_SUCCESS)
{
    return mosquitto_error(rc);
}
printf("Publish returned OK\r\n");

자세한 내용은 디바이스에서 클라우드로 메시지 보내기를 참조하세요.

클라우드-디바이스 메시지 수신

mosquitto_subscribe 샘플은 MQTT 라이브러리를 사용하여 MQTT 항목을 구독하고 IoT 허브에서 클라우드-디바이스 메시지를 수신하는 방법을 보여 줍니다.

mosquitto_subscribe 샘플을 실행합니다. 예를 들어, Linux에서:

./build/mosquitto_subscribe

다음 명령을 실행하여 IoT Hub에서 클라우드-디바이스 메시지를 보냅니다. IoT Hub의 이름을 사용해야 합니다.

az iot device c2d-message send --hub-name my-hub --device-id mqtt-dev-01 --data "hello world"

mosquitto_subscribe의 출력은 다음 예와 같습니다.

Waiting for C2D messages...
C2D message 'hello world' for topic 'devices/mqtt-dev-01/messages/devicebound/%24.mid=d411e727-...f98f&%24.to=%2Fdevices%2Fmqtt-dev-01%2Fmessages%2Fdevicebound&%24.ce=utf-8&iothub-ack=none'
Got message for devices/mqtt-dev-01/messages/# topic

코드 검토

다음 코드 조각은 mosquitto/src/mosquitto_subscribe.cpp 파일에서 가져온 것입니다.

다음 문은 디바이스에서 클라우드-디바이스 메시지를 수신하는 데 사용하는 항목 필터를 정의합니다. #은 다단계 와일드카드입니다.

#define DEVICEMESSAGE "devices/" DEVICEID "/messages/#"

main 함수는 mosquitto_message_callback_set 함수를 사용하여 콜백을 설정하여 IoT Hub에서 보낸 메시지를 처리하고 mosquitto_subscribe 함수를 사용하여 모든 메시지를 구독합니다. 다음 코드 조각은 콜백 함수를 보여 줍니다.

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("C2D message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    bool match = 0;
    mosquitto_topic_matches_sub(DEVICEMESSAGE, message->topic, &match);

    if (match)
    {
        printf("Got message for " DEVICEMESSAGE " topic\r\n");
    }
}

자세한 내용은 MQTT를 사용하여 클라우드-디바이스 메시지 수신을 참조하세요.

디바이스 쌍 업데이트

mosquitto_device_twin 샘플은 디바이스 쌍에서 reported 속성을 설정한 다음 속성을 다시 읽는 방법을 보여 줍니다.

mosquitto_device_twin 샘플을 실행합니다. 예를 들어, Linux에서:

./build/mosquitto_device_twin

mosquitto_device_twin의 출력은 다음 예와 같습니다.

Setting device twin reported properties....
Device twin message '' for topic '$iothub/twin/res/204/?$rid=0&$version=2'
Setting device twin properties SUCCEEDED.

Getting device twin properties....
Device twin message '{"desired":{"$version":1},"reported":{"temperature":32,"$version":2}}' for topic '$iothub/twin/res/200/?$rid=1'
Getting device twin properties SUCCEEDED.

코드 검토

다음 코드 조각은 mosquitto/src/mosquitto_device_twin.cpp 파일에서 가져온 것입니다.

다음 문은 디바이스가 디바이스 쌍 업데이트를 구독하고 디바이스 쌍을 읽고 디바이스 쌍을 업데이트하는 데 사용하는 항목을 정의합니다.

#define DEVICETWIN_SUBSCRIPTION  "$iothub/twin/res/#"
#define DEVICETWIN_MESSAGE_GET   "$iothub/twin/GET/?$rid=%d"
#define DEVICETWIN_MESSAGE_PATCH "$iothub/twin/PATCH/properties/reported/?$rid=%d"

main 함수는 mosquitto_connect_callback_set 함수를 사용하여 콜백을 설정하여 IoT Hub에서 보낸 메시지를 처리하고 mosquitto_subscribe 함수를 사용하여 $iothub/twin/res/# 항목을 구독합니다.

다음 코드 조각은 mosquitto_publish를 사용하여 디바이스 쌍에서 reported 속성을 설정하는 connect_callback 함수를 보여 줍니다. 디바이스가 메시지를 $iothub/twin/PATCH/properties/reported/?$rid=%d 항목에 게시합니다. 디바이스가 항목에 메시지를 게시할 때마다 %d 값이 증가합니다.

void connect_callback(struct mosquitto* mosq, void* obj, int result)
{
    // ... other code ...  

    printf("\r\nSetting device twin reported properties....\r\n");

    char msg[] = "{\"temperature\": 32}";
    char mqtt_publish_topic[64];
    snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_PATCH, device_twin_request_id++);

    int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
    if (rc != MOSQ_ERR_SUCCESS)

    // ... other code ...  
}

디바이스는 $iothub/twin/res/# 항목을 구독하고 IoT Hub에서 메시지를 수신하면 message_callback 함수가 이를 처리합니다. 샘플을 실행하면 message_callback 함수가 두 번 호출됩니다. 처음으로 디바이스는 reported 속성 업데이트에 대한 IoT Hub의 응답을 받습니다. 그런 다음 디바이스는 디바이스 쌍을 요청합니다. 두 번째로 디바이스는 요청된 디바이스 쌍을 받습니다. 다음 코드 조각은 message_callback 함수를 보여 줍니다.

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("Device twin message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    const char patchTwinTopic[] = "$iothub/twin/res/204/?$rid=0";
    const char getTwinTopic[]   = "$iothub/twin/res/200/?$rid=1";

    if (strncmp(message->topic, patchTwinTopic, sizeof(patchTwinTopic) - 1) == 0)
    {
        // Process the reported property response and request the device twin
        printf("Setting device twin properties SUCCEEDED.\r\n\r\n");

        printf("Getting device twin properties....\r\n");

        char msg[] = "{}";
        char mqtt_publish_topic[64];
        snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_GET, device_twin_request_id++);

        int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
        if (rc != MOSQ_ERR_SUCCESS)
        {
            printf("Error: %s\r\n", mosquitto_strerror(rc));
        }
    }
    else if (strncmp(message->topic, getTwinTopic, sizeof(getTwinTopic) - 1) == 0)
    {
        // Process the device twin response and stop the client
        printf("Getting device twin properties SUCCEEDED.\r\n\r\n");

        mosquitto_loop_stop(mosq, false);
        mosquitto_disconnect(mosq); // finished, exit program
    }
}

자세한 내용은 MQTT를 사용하여 디바이스 쌍 reported 속성 업데이트MQTT를 사용하여 디바이스 쌍 속성 검색을 참조하세요.

리소스 정리

더 많은 디바이스 개발자 문서를 계속할 계획이라면 이 문서에서 사용한 리소스를 유지하고 재사용할 수 있습니다. 그렇지 않으면 이 문서에서 만든 리소스를 삭제하여 추가 요금을 방지할 수 있습니다.

다음 Azure CLI 명령을 사용하여 전체 리소스 그룹을 삭제하면 허브와 등록된 디바이스를 한 번에 삭제할 수 있습니다. 이러한 리소스가 유지하려는 다른 리소스와 리소스 그룹을 공유하는 경우에는 이 명령을 사용하지 마세요.

az group delete --name <YourResourceGroupName>

IoT 허브만 삭제하려면 Azure CLI을 사용하여 다음 명령을 실행합니다.

az iot hub delete --name <YourIoTHubName>

IoT 허브에 등록한 디바이스 ID만 삭제하려면 Azure CLI를 사용하여 다음 명령을 실행합니다.

az iot hub device-identity delete --hub-name <YourIoTHubName> --device-id <YourDeviceID>

개발 머신에서 복제된 샘플 파일을 제거할 수도 있습니다.

다음 단계

이제 Mosquitto MQTT 라이브러리를 사용하여 IoT Hub와 통신하는 방법을 알아보았으니, 다음 단계로 GitHub에서 MQTT 애플리케이션 샘플을 살펴보는 것이 좋습니다.