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

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

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

이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.

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

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

Azure 구독이 아직 없는 경우 시작하기 전에 체험 계정을 만듭니다.

필수 조건

Azure CLI에 대한 환경 준비

개발 컴퓨터 필수 조건

Windows를 사용하는 경우:

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

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

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

Linux를 사용하는 경우:

  1. 다음 명령을 실행하여 빌드 도구를 설치합니다.

    sudo apt install cmake g++
    
  2. 다음 명령을 실행하여 Mosquitto 클라이언트 라이브러리를 설치합니다.

    sudo apt install libmosquitto-dev
    

환경 설정

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을 참조하세요.

샘플 리포지토리 복제

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

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와 통신하는 방법을 배웠으므로 제안된 다음 단계는 다음을 검토하는 것입니다.