09주차 ‐ MQTT 실습 - gitjs523/SmartDevice2025 GitHub Wiki

1. MQTT 개요

(1) MQTT란?

  • MQTT: Message Queuing Telemetry Transport. 사물 인터넷(IoT)응용 프로그램과 머신 투 머신(Machine-to-Machine) 통신을 위해 고안된 경량 메시지 통신 프로토콜.
  • 발행(Publish) 와 구독(Subscribe) 기반으로 동작
  • 낮은 대역폭, 높은 신뢰성, 적은 전력 소비가 필요한 환경에 최적화
  • 장치는 메시지를 토픽 에 발행하고, 다른 장치들은 해당 토픽을 구독하여 메시지 수신
  • 통신은 브로커(Broker)라는 중개 서버 이용
  • 사용 예시:
    • 스마트 홈(조명, 온도 제어)
    • 차량 상태 모니터링
    • 웨어러블 기기

(2) MQTT의 통신 구조

  • 통신 구조: [Publisher] → [Broker] → [Subscriber]
    • Publisher: 특정 토픽에 메시지 발행
    • Broker: 메시지를 수신 후 구독자에게 전달
    • Subscriber: 관심 있는 토픽 구독하고 메시지 수신

(3) MQTT의 특징

  1. 경량 프로토콜: 최소한의 네트워크 트래픽
  2. 항상 연결 상태 유지: 장기 연결 기반 통신 지원
  3. 빠른 반응성: 실시간 데이터 전달 가능
  4. 저전력 소모: 배터리 기반 기기에 적합
  5. QoS(Quality of Service) 제공: 신뢰성 있는 데이터 전송 설정 가능
  6. 확장성: 수백만 개의 디바이스에 연결 가능

(4) MQTT 관련 용어

  • Client: MQTT 통신에 참여하는 장치 또는 프로그램
  • Broker: 클라이언트 간 메시지 중계 서버
  • Publish: 토픽에 메시지를 보내는 행위
  • Subscribe: 특정 토픽에 대해 메시지를 받겠다고 등록하는 행위
  • Topic: 메시지를 주고받는 가상의 채널 경로
  • Payload: 실제로 전송되는 데이터 내용
  • QoS(Quality of Service): 메시지 전송의 신뢰성 수준 설정(예: 0 ~ 2 레벨로 설정)

2. MQTT 실습

(1) 초음파 센서를 이용한 거리 측정

  • ESP32, ESP32 확장 쉴드, 초음파 센서 필요
  • 실습 목표: 1초 간격으로 물체와 초음파 센서 사이의 거리를 출력

사용한 코드

const int trigPin = 12;  // 초음파 센서의 트리거 핀
const int echoPin = 14;  // 초음파 센서의 에코 핀

long duration;          // 음파의 왕복 시간(마이크로초)
float distanceCm;       // 거리(cm)
float distanceInch;     // 거리(인치)

void setup() {
  Serial.begin(115200); // 시리얼 통신 시작 (통신 속도: 115200bps)
  pinMode(trigPin, OUTPUT);  // 트리거 핀을 출력으로 설정
  pinMode(echoPin, INPUT);   // 에코 핀을 입력으로 설정
}

void loop() {
  digitalWrite(trigPin, LOW);           // 트리거 핀 LOW로 초기화
  delayMicroseconds(2);                 // 2 마이크로초 대기
  digitalWrite(trigPin, HIGH);          // 트리거 핀 HIGH로 설정하여 초음파 송신
  delayMicroseconds(10);                // 10 마이크로초 동안 
  digitalWrite(trigPin, LOW);           // 초음파 송신 종료
  
  duration = pulseIn(echoPin, HIGH);     // 에코 핀에서 초음파의 왕복 시간 측정
  
  distanceCm = duration * 0.034/2; // 거리 계산
  
  distanceInch = distanceCm * 0.393701; // 인치로 변환
  
  Serial.print("Distance: ");
  Serial.print(distanceCm);              // 거리 (cm)
  Serial.print("(cm)  ");
  Serial.print(distanceInch);            // 거리 (인치)
  Serial.println("(inch)");
  
  delay(1000);                           // 1초 대기
}
  • 손을 초음파 센서에 가까이 위치해보며 거리 측정

실행 결과

20250511_220205.mp4

image

(2) 초음파 센서 데이터 MQTT로 Publish

  • 목표: 초음파 센서로 거리 측정한 값을 MQTT Explorer에서 확인해보기

준비사항

  • 라이브러리 설치: [PubSubClient by Nick O’Leary]
  • WiFi 정보 설정: SSID, PASSWORD 수정
  • MQTT Explorer 설치 및 실행
  • 실행 후 초음파 센서 값을 "user1/esp32/ultra" 토픽으로 검색 후 Publish

사용한 코드

#include <WiFi.h>          //Wi-Fi 연결 관련 라이브러리
#include <PubSubClient.h>  //MQTT 프로토콜을 사용하기 위한 라이브러리

// 다음 변수들을 당신의 SSID와 비밀번호로 대체하세요.
const char* ssid = "YourSSID";          // 사용하는 WiFi 네트워크 이름 (SSID)
const char* password = "Password";  // 사용하는 WiFi 네트워크 비밀번호

const int trigPin = 12;  // 초음파 센서의 트리거 핀
const int echoPin = 14;  // 초음파 센서의 에코 핀

// MQTT 브로커 IP 주소를 여기에 입력하세요 (예: "192.168.1.144")
const char* mqtt_server = "test.mosquitto.org";
const int mqttPort = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

long lastMsgTime = 0;

void setup() {
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, mqttPort);

  pinMode(echoPin, INPUT);  // 에코 핀을 입력으로 설정
  // 트리거 핀 초기화
  pinMode(trigPin, OUTPUT);
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
}

void setup_wifi() {
  delay(10);
  // Wi-Fi 네트워크에 연결 시작
  Serial.println();
  Serial.print("연결 중인 Wi-Fi: ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

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

  Serial.println("");
  Serial.println("Wi-Fi 연결됨");
  Serial.println("IP 주소: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // 연결이 될 때까지 반복
  while (!client.connected()) {
    Serial.print("MQTT 연결 시도 중...");
    // 랜덤 클라이언트 ID 생성
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);
    Serial.print("클라이언트 ID: ");
    Serial.println(clientId);
    // 연결 시도
    if (client.connect(clientId.c_str())) {
      Serial.println("연결됨");
    } else {
      Serial.print("실패, rc=");
      Serial.print(client.state());
      Serial.println(" 5초 후 다시 시도");
      // 5초 대기 후 다시 시도
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  // 클라이언트가 메시지를 처리하고 서버와 연결 유지
  client.loop();

  long now = millis();
  if (now - lastMsgTime > 1000) {  //1초 간격
    lastMsgTime = now;
    // 초음파 센서 값을 읽어옵니다.
    float sensorValue = readUltrasonicSensor();
    char sensorString[8];
    dtostrf(sensorValue, 1, 2, sensorString);
    client.publish("user1/esp32/ultra", sensorString);
  }
}

float readUltrasonicSensor() {
  // 트리거 핀을 10 마이크로초 동안 HIGH로 설정하여 초음파를 발사합니다.
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // 에코 핀에서 펄스의 지속 시간을 측정합니다.
  pinMode(echoPin, INPUT);
  float duration = pulseIn(echoPin, HIGH);

  // 소리의 속도를 기준으로 거리를 계산합니다.(343m/s로 가정)
  float distance = duration * 0.0343 / 2.0;

  return distance;
}

실행 결과

20250511_224621.mp4

손을 가까이 하자 그래프의 값이 대체로 낮아졌다

image

(3) 능동 부저 제어

  • 준비물:
    • ESP32
    • ESP32 확장 쉴드
    • 능동 부저: 전기 신호를 주면 소리가 나는 장치

사용한 코드

const int buzzerPin = 2; //led 핀 번호 설절
 
void setup (){
  pinMode (buzzerPin,OUTPUT );//buzzerPin 을 출력으로 설정
}
 
void loop (){
  digitalWrite (buzzerPin, HIGH ); //buzzerPin 에 HIGH 값 쓰기
  delay (500 ); // 0.5 초 기다리기
  digitalWrite (buzzerPin, LOW ); //buzzerPin 에 LOW 값 쓰기
  delay (1000 ); // 1 초 기다리기
}

실행결과

20250511_230704.mp4

(4) 능동 부저 제어 Subscriber 만들기

  • 능동 부저를 MQTT Explorer와 연결하여 제어하기
  • 토픽: "user1/esp32/buzzer" 구독해야 함
  • 1 값을 넣으면 부저 켜짐/ 0 값을 넣으면 부저 꺼짐

image

사용한 코드

#include <WiFi.h>
#include <PubSubClient.h>
 
// 다음 변수들을 당신의 SSID와 비밀번호로 대체하세요.
const char* ssid = "YourSSID";
const char* password = "Password";

// MQTT 브로커 IP 주소를 여기에 입력하세요 (예: "192.168.1.144")
const char* mqtt_server = "test.mosquitto.org";
const int mqttPort = 1883 ;
const char * mqttTopic = "user1/esp32/buzzer"; // 사용자에 맞게 변경
const int buzzerPin = 2 ; // 부저에 연결된 GPIO 핀 번호
 
WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi() {
  delay(10);
  // Wi-Fi 네트워크에 연결 시작
  Serial.println();
  Serial.print("연결 중인 Wi-Fi: ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

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

  Serial.println("");
  Serial.println("Wi-Fi 연결됨");
  Serial.println("IP 주소: ");
  Serial.println(WiFi.localIP());
}
 
void setup(){
    // 시리얼 통신 초기화
  Serial.begin(115200);
  // Wi-Fi 연결 설정
  setup_wifi();

  client.setServer(mqtt_server, mqttPort);
  client.setCallback(callback);
 
  // 부저 핀을 출력 모드로 설정
  pinMode(buzzerPin,OUTPUT);
}
 
void loop(){
  if(!client.connected()){
    reconnect();
  }
  client.loop();//MQTT 클라이언트를 유지하기 위해 호출
}
 
void callback(char*topic , byte *payload , unsigned int length){
  Serial.print("Receivedmessage: ");
  Serial.print(topic);
  Serial.print(" ");
  for(int i = 0 ; i <length;i++){
    Serial.print((char)payload[i]);
  }
  Serial.println();
 
  if(strcmp(topic, mqttTopic)== 0){
    if(payload[0] == '1'){
     // 부저를 켜는 코드 작성
     digitalWrite(buzzerPin, HIGH); 
    }else if(payload[0] == '0'){
     // 부저를 끄는 코드 작성
     digitalWrite(buzzerPin, LOW); 
    }
  }
}
 
void reconnect(){
  while(!client.connected()){
    Serial.print("Connectingto MQTT Broker...");
   String clientId = "ESP32Client-";
   clientId += String(random(0xffff), HEX);
    if(client.connect(clientId.c_str())){
     Serial.println("Connected to MQTT Broker");
     client.subscribe(mqttTopic);
    }else {
     Serial.print("Failed, rc=");
     Serial.print(client.state());
     Serial.println("Retrying in 5 seconds...");
     delay(5000);
    }
  }
}

실행 결과

20250511_232158.mp4
⚠️ **GitHub.com Fallback** ⚠️