반응형

MQTT 개념과 구현 예제를 다룹니다.

이름만 들어봤던 것을 마침 개념을 정확히 알아둘 필요가 생겨서 정리해봤습니다.

아직 예제 코드 및 MQTT 예제는 실행하여 확인해보지는  않았습니다.



다음 링크의 문서를 번역한 내용을 바탕으로 이해한 것을 보충했습니다.

https://learn.sparkfun.com/tutorials/introduction-to-mqtt/all 



추가적으로 아래 링크를 참고했습니다.

https://khj93.tistory.com/entry/MQTT-MQTT의-개념 



2022. 2. 23  최초작성 



MQTT는 네트워크에 있는  IoT 디바이스간에 메시지를 주고 받기 위한 프로토콜입니다. 





주요 개념

몇가지 개념을 알아둘 필요가 있습니다.

 

Broker - 브로커는 서버에 연결된 구독자 클라이언트와 발행자 클라이언트 간에 메시지를 전송하는 역할을 합니다.  

 

Client - 브로커에 연결하여 정보를 보내거나 받는 장치입니다. 메시지를 전송하는 발행자 클라이언트와 메시지를 수신하는 구독자 클라이언트가 될 수 있습니다.  발행자 클라이언트는 토픽을 통해 브로커에게 메시지를 전송하고 브로커는 특정 토픽을 구독한 구독자 클라이언트에게 메시지를 전송하게 됩니다.  구독자 클라이언트는 주기적으로 체크하는 폴링 방식으로 브로커에서 구독한 토픽에 해당되는 메시지를 수신하게 됩니다.

 

Publish - 토픽을 구독한 클라이언트에게 메시지를 송신하기 위해 브로커에 메시지를 송신하는 클라이언트입니다.

 

Subscribe - 클라이언트는 브로커에게 관심 있는 토픽을 구독한다고 알려줍니다. 

클라이언트가 토픽을 구독하면 브로커에 배포된 해당 토픽의 모든 메시지가 해당 토픽 구독자(subscribers)에게 송신됩니다.

클라이언트는 해당 토픽에 대한 브로커로부터의 메시지 수신을 중지하기 위해 구독을 취소할 수도 있습니다.

 

Topic - 메시지의 이름입니다. 클라이언트는 토픽을 게시(publish), 구독(subsctibe) 또는 둘을 동시에 수행할 수 있습니다. 

 

QoS - 서비스 품질(Quality of Service)

각 연결(connection)은 0 ~ 2 사이의 정수 값을 지정하여 브로커에 대한 서비스 품질을 지정할 수 있습니다.

QoS는 TCP 데이터 전송 처리에 영향을 주지 않고 MQTT 클라이언트 사이에서만 영향을 미칩니다.

 

0 으로 지정하면 최대 한번 송신 하며, 정상적으로 보내졌는지 확인하지 않습니다. 

 

1 으로 지정하면 적어도 한번 송신하며 정상적으로 보내졌는지 확인될 때가지 여러 번 전송합니다. 

같은 메시지를 중복 송신할 가능성이 있습니다 

 

2 를 지정하면 메시지를 한 번만 송신합니다. 발신자 및 수신자 클라이언트가 2단계 핸드쉐이크를 사용하기 때문에 송신 클라이언트는 이미 메시지를 보낸 것을 알고 있기 때문에 수신자 클라이언트가 메시지를 못받았다고 중복 송신을 하지는 않습니다. 



MQTT 동작

MQTT는 발행/구독 메시징 프로토콜(publish/subcribe messaging protocol)입니다. 

 

MQTT가 동작하려면 클라이언트가 토픽을 구독하거나 발행할 수 있는 네트워크에 연결되어야 합니다.  클라이언트가 토픽을 게시하면 메시지가 브로커로 전송되기 시작하고 브로커는 해당 주제를 구독한 모든 클라이언트에게 해당 메시지를 전송합니다. 

 

토픽은 디렉토리와 같은 구조로 상위 토픽과 하위 토픽으로 구성되어 배치됩니다. 상위 토픽 내에 여러 클라이언트가 있는 경우 토픽은 "LivingRoom" 또는 "LivingRoom/Light"일 수 있습니다.

 

아래 그림은 상위 토픽과 하위 토픽으로 구성된 예입니다.

 

출처 - https://khj93.tistory.com/entry/MQTT-MQTT의-개념 



구독자 클라이언트는 구독된 주제에서 들어오는 메시지를 수신하고  "켜기" 또는 "끄기"와 같이 해당 토픽에 게시된 내용에 반응하여 전등을 제어하도록 할 수 있습니다.

 

클라이언트는 하나의 토픽을 구독한 상태에서 다른 토픽을 게시할 수 있습니다. 클라이언트가 조명을 제어하는 “LivingRoom/Light"을 구독하는 경우 다른 클라이언트가 해당 조명의 상태를 모니터링할 수 있도록  조명의 상태를 배포하기 위해 "LivingRoom/Light/State"와 같은 다른 토픽에 게시할 수 있습니다.



이미지 출처 - https://underflow101.tistory.com/22 



이제 MQTT가 작동하는 방식에 대한 이론을 이해했으므로 Raspberry Pi 및 ESP32 Thing 보드를 사용하여 예제를 빌드하여 작동하는 모습을 확인하겠습니다. 브로커를 설정하고 테스트를 실행하여 올바르게 작동하는지 확인합니다.

 

브로커 세팅

본 문서에서 사용된 예제에서는 로컬 네트워크에 연결된 Raspberry Pi에서 Mosquitto라는 무료 오픈 소스 브로커를 실행합니다.



Mosquitto를 다음처럼 설치합니다.

 

sudo apt-get install mosquitto -y



mosquitto client를 설치합니다. 

 

sudo apt-get install mosquitto mosquitto-clients -y



이제 다음처럼 "test_topic" 토픽을 구독합니다.

 

mosquitto_sub -t "test_topic"



현재 명령을 실행한 터미널에서 브로커에서 수신된 메시지를 출력하기 때문에 메시지를 게시하려면 두 번째 터미널 창을 열어야 합니다. 열리면 다음 명령을 사용하여 토픽 test_topic에 메시지를 게시합니다.

 

“test_topic” 토픽에 “HELLO WORLD!” 메시지를 게시합니다. 

 

mosquitto_pub -t "test_topic" -m "HELLO WORLD!"

 



클라이언트 세팅

이제 브로커가 실행 중인 상태이니 클라이언트를 추가할 차례입니다.

 

우리는 두 개의 클라이언트를 만들 것입니다. 

첫 번째 클라이언트(Publish Client - Switch)는 버튼을 누를 때마다 "on" 또는 "off" 메시지를 "room/light" 주제에 게시됩니다.

 

두 번째 클라이언트(Subscribe Client - Light)는  "room/light" 토픽을 구독하고 수신된 메시지에 따라 LED를 켜거나 끄고 현재 LED 상태를 "room/light/state" 토픽에 게시합니다. 



Publish Client - Switch

스위치 눌러진 상태를 메시지로 전송하는 역할을 하는 클라이언트 생성하기 위해 ESP32을 사용합니다.  ESP32에서 MQTT가 작동하도록 하려면 아래 링크에서 다운로드할 수 있는 PubSubClient라는 라이브러리를 설치해야 합니다.



공유기의 WiFi 인증 정보과 Raspberry Pi 브로커의 IP 주소를 코드에서 수정해야 합니다. 

 

ESP32가 네트워크에 연결된 후, 버튼이 눌러지면 ESP32는 "room/light" 토픽에 “on” 메시지를 게시하고 버튼에서 손을 떼면 “off” 메시지를 게시합니다. 

 

/******************************************************************************
MQTT_Switch_Example.ino
Example for controlling a light using an MQTT switch
by: Alex Wende, SparkFun Electronics

This sketch connects the ESP32 to a MQTT broker and subcribes to the topic
room/light. When the button is pressed, the client will toggle between
publishing "on" and "off".
******************************************************************************/

#include <WiFi.h>
#include <PubSubClient.h>

const char *ssid =  "-----";   // name of your WiFi network
const char *password =  "-----"; // password of the WiFi network

const byte SWITCH_PIN = 0;           // Pin to control the light with
const char *ID = "Example_Switch"// Name of our device, must be unique
const char *TOPIC = "room/light"// LED 제어 명령 메시지를 게시할 토픽 이름

IPAddress broker(192,168,1,-); // IP address of your MQTT broker eg. 192.168.1.50
WiFiClient wclient;

PubSubClient client(wclient); // Setup MQTT client
bool state=0;

// Connect to WiFi network
void setup_wifi() {
  Serial.print("\nConnecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password); // Connect to network

  while (WiFi.status() != WL_CONNECTED) { // Wait for connection
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// Reconnect to client
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(ID)) {
      Serial.println("connected");
      Serial.print("Publishing to: ");
      Serial.println(TOPIC);  // 게시할 토픽을 등록하는 부분이 없네요. 
      Serial.println('\n');

    } else {
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200); // Start serial communication at 115200 baud
  pinMode(SWITCH_PIN,INPUT);  // Configure SWITCH_Pin as an input
  digitalWrite(SWITCH_PIN,HIGH);  // enable pull-up resistor (active low)
  delay(100);
  setup_wifi(); // Connect to network
  client.setServer(broker, 1883); // 브로커를 설정합니다
}

void loop() {
  if (!client.connected())  // Reconnect if connection is lost
  {
    reconnect();
  }
  client.loop();

  // 스위치 상태를 읽음
  if(digitalRead(SWITCH_PIN) == 0)
  {
    state = !state; //toggle state
    if(state == 1) // 버튼이 눌러진 경우
    {
      client.publish(TOPIC, "on"); // on 메시지를 토픽에 게시합니다.
      Serial.println((String)TOPIC + " => on");
    }
    else // 버튼이 안눌러진 경우
    {
      client.publish(TOPIC, "off"); // off 메시지를 토픽에 게시합니다.
      Serial.println((String)TOPIC + " => off");
    }

    while(digitalRead(SWITCH_PIN) == 0) // Wait for switch to be released
    {
      // Let the ESP handle some behind the scenes stuff if it needs to
      yield();
      delay(20);
    }
  }
}



코드를 업로드 후, ESP32를 네트워크에 연결합니다. 이제 브로커가 설치되어 있는 라즈베리파이의 터미널 창에서 다음 명령을 사용하여 "room/light" 토픽을 구독합니다.

 

mosquitto_sub -t "room/light"

 

이제 GPIO 핀 0에 연결된 ESP32의 버튼을 누르면 아래와 같이 켜짐/꺼짐 명령이 표시됩니다.

 



Subscribe Client - Light

이제 스위치가 브로커에 연결되었으므로  구독한 토픽의 메시지를 수신하여 LED를 동작하게 만들 장치를 준비합니다.

또 하나의 ESP32를 준비하여 GPIO 핀 5에 연결된 LED를 제어하도록 합니다. 

 

공유기의 WiFi 인증 정보과 Raspberry Pi 브로커의 IP 주소를 코드에서 수정해야 합니다. 


/******************************************************************************
MQTT_Light_Example.ino
Example for controlling a light using MQTT
by: Alex Wende, SparkFun Electronics

This sketch connects the ESP8266 to a MQTT broker and subcribes to the topic
room/light. When "on" is recieved, the pin LIGHT_PIN is toggled HIGH.
When "off" is recieved, the pin LIGHT_PIN is toggled LOW.
******************************************************************************/

#include <WiFi.h>
#include <PubSubClient.h>

const char *ssid = "-----";   // name of your WiFi network
const char *password = "-----"; // password of the WiFi network

const byte LIGHT_PIN = 5;           // LED가 연결된 핀
const char *ID = "Example_Light"// Name of our device, must be unique
const char *TOPIC = "room/light"// LED 명령을 구독할 토픽
const char *STATE_TOPIC = "room/light/state"// LED 상태를 게시할 토픽

IPAddress broker(192,168,1,-); // IP address of your MQTT broker eg. 192.168.1.50
WiFiClient wclient;

PubSubClient client(wclient); // Setup MQTT client

// 브로커에서 메시지가 수신되면 호출됩니다.
void callback(char* topic, byte* payload, unsigned int length) {
  String response;

  for (int i = 0; i < length; i++) {
    response += (char)payload[i];  // 브로커에서 수신된 메시지에서 LED 제어 명령을 꺼냅니다.
  }
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  Serial.println(response);
  if(response == "on"// 수신된 메시지가 on인 경우
  {
    digitalWrite(LIGHT_PIN, HIGH);  // LED를 켜진 상태로 만듭니다.
    client.publish(STATE_TOPIC,"on");  // LED 상태를 on으로 게시합니다.
  }
  else if(response == "off"// 수신된 메시지가 off인 경우
  {
    digitalWrite(LIGHT_PIN, LOW);   // LED를 꺼진 상태로 만듭니다.
    client.publish(STATE_TOPIC,"off"); // LED 상태를 off로 게시합니다.
  }
}

// Connect to WiFi network
void setup_wifi() {
  Serial.print("\nConnecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password); // Connect to network

  while (WiFi.status() != WL_CONNECTED) { // Wait for connection
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

// Reconnect to client
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if(client.connect(ID)) {
      client.subscribe(TOPIC);  // 토픽을 구독합니다.
      Serial.println("connected");
      Serial.print("Subcribed to: ");
      Serial.println(TOPIC);
      Serial.println('\n');

    } else {
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200); // Start serial communication at 115200 baud
  pinMode(LIGHT_PIN, OUTPUT); // Configure LIGHT_PIN as an output
  delay(100);
  setup_wifi(); // Connect to network
  client.setServer(broker, 1883); // 브로커를 설정합니다.
  client.setCallback(callback);// Initialize the callback routine
}

void loop() {
  if (!client.connected())  // Reconnect if connection is lost
  {
    reconnect();
  }
  client.loop();
}



두 번째 ESP32에서 GPIO 핀 5에 연결된 LED 위치를 확인합니다. 

 

두 번째 ESP32가 네트워크에 연결되면 자동으로 "room/light" 토픽에 구독되고 첫 번째 ESP32의 버튼을 누르면 두 번째 ESP32의 GPIO 핀 5에 연결된 LED가 켜져야 합니다.



반응형

진행해본 결과물을 기록 및 공유하는 공간입니다.
잘못된 부분이나 개선점을 알려주시면 반영하겠습니다.


소스코드 복사시 하단에 있는 앵커 광고의 왼쪽 위를 클릭하여 닫은 후 해야 합니다.


문제가 생기면 포스트와 바뀐 환경이 있나 먼저 확인해보세요.
질문을 남겨주면 가능한 빨리 답변드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기

댓글을 달아 주세요

TistoryWhaleSkin3.4">