스마트 디바이스 9주차 MQTT 초음파센서 실습 - yubiine/25-1_smartdevice GitHub Wiki

⭐목표

  • MQTT를 활용한 IoT 서비스 구현

🔹MQTT란?

  • MQTT(Message Queuing Telemetry Transport) 는 경량 메시지 통신 프로토콜로,
    제한된 네트워크 환경에서도 빠르고 안정적인 통신이 가능한 방식입니다. 주로 IoT(사물인터넷) 장치 간 통신에 사용됩니다.
  • 1999년 IBM에서 처음 개발되었으며,
    낮은 대역폭, 높은 신뢰성, 적은 전력 소비가 필요한 환경에 최적화되어 있습니다.

🔹MQTT 핵심 개념

✅1. Pub/Sub 모델 (Publish / Subscribe)

MQTT는 전통적인 클라이언트-서버 방식이 아니라 메시지를 발행하고 구독하는 구조입니다.

[Publisher] → [Broker] → [Subscriber]

  • Publisher (발행자): 특정 주제(topic)에 메시지를 보냅니다.

  • Subscriber (구독자): 특정 topic을 구독하고, 그에 대한 메시지를 받습니다.

  • Broker (중개자): Publisher가 보낸 메시지를 받아서 해당 topic을 구독 중인 Subscriber들에게 전달합니다.

image

이 구조는 여러 장치 간의 효율적이고 유연한 통신을 가능하게 합니다.


✅2. Topic (토픽) 기반 라우팅

MQTT에서는 메시지가 string 형식의 토픽에 따라 전송됩니다.

예: "sensor/room1/temperature"

Subscriber는 해당 토픽을 구독하면 해당 토픽으로 발행된 메시지를 수신합니다.

토픽은 디렉토리 구조처럼 구분("/")되고, 와일드카드(+, #)도 사용 가능합니다.


✅3. Broker의 역할

Broker는 핵심 중개자입니다. MQTT 장치들(Publisher/Subscribers)은 서로 직접 통신하지 않고, Broker를 통해서만 통신합니다.

대표적인 MQTT Broker:

  • Mosquitto (오픈소스)
  • HiveMQ (상업용 및 무료)
  • AWS IoT Core, Google Cloud IoT 등 클라우드 서비스

✅4. 메시지 QoS (Quality of Service)

MQTT는 전송 신뢰도를 3단계로 설정할 수 있습니다.

QoS 수준 설명
0 (At most once) 최대 한 번 전송, 손실될 수 있음. 가장 빠름.
1 (At least once) 한 번 이상 전달, 중복 가능성 있음.
2 (Exactly once) 정확히 한 번만 전달, 가장 신뢰성 높지만 느림.

네트워크 품질이나 데이터 중요도에 따라 QoS를 선택할 수 있습니다.


🔹MQTT 주요 특징

특징 설명
경량성 (Lightweight) 매우 작은 헤더(2바이트), 텍스트 기반 프로토콜 아님. 저사양, 배터리 기반 장치에서도 적합.
낮은 대역폭 사용 텍스트가 아니라 이진 메시지를 사용. 네트워크가 느려도 효과적으로 작동.
유지 연결 (Persistent Connection) MQTT는 연결을 계속 유지하며 메시지를 지속적으로 수신하거나 발행함.
Last Will and Testament (LWT) 클라이언트가 비정상 종료되었을 때, 미리 정의한 메시지를 Broker가 대신 발행하여 상태 알림.
Retained Messages 가장 최근 메시지를 Broker가 저장해두고, 새로 연결한 Subscriber에게 즉시 전달.
Session 유지 클라이언트는 재연결 시에도 이전의 구독 상태를 유지할 수 있음. (Clean Session = false 일 때)
보안 (Security) 기본적으로 암호화 없음. 하지만 TLS/SSL을 추가하거나 인증 토큰 사용 가능.
확장성 수천 개의 장치를 동시에 관리할 수 있으며, 클러스터링도 가능함.

🔹MQTT vs HTTP

항목 MQTT HTTP
프로토콜 방식 메시지 기반, 발행/구독(Pub/Sub) 요청/응답(Request/Response) 기반
연결 상태 지속 연결 (Keep-Alive) 매 요청마다 연결
메시지 크기 작음 (2바이트 헤더) 큼 (헤더 + 본문 포함)
통신 방향 양방향 (Broker를 통한 Push 지원) 단방향 (클라이언트가 요청 시에만)
신뢰성(QoS) QoS 0, 1, 2 지원 (메시지 전송 보장) 기본 보장 없음 (애플리케이션 레벨 처리)
실시간성 매우 우수 (즉시 전송) 낮음 (주기적 요청 필요)
배터리 효율 높음 (지속 연결 유지, 오버헤드 적음) 낮음 (자주 연결하고 끊어야 함)
데이터 전송 빈도 빈번한 전송에 적합 느린 빈도에 적합
보안 TLS/SSL 적용 가능 HTTPS (TLS 기반) 기본 적용
서버 MQTT Broker 필요 일반 웹서버 가능

따라서 MQTT는 센서, 홈 IoT, 스마트팩토리, 모바일 디바이스에 특히 적합합니다.

🔹MQTT와 HTTP의 적합

✅ MQTT가 적합한 상황

상황 이유
🔁 지속적인 데이터 송수신 필요 센서에서 실시간으로 데이터를 보내야 할 때 (ex. 온도, 거리, GPS 등)
📡 IoT 장치가 자주 메시지를 주고받는 환경 Pub/Sub 구조로 효율적이며, Broker가 중개하여 분산에 강함
🔌 네트워크가 불안정하거나 제한된 환경 메시지 유실 방지(QoS), 재전송, 저전력 통신
🔋 배터리로 동작하는 저전력 장치 낮은 오버헤드, 연결 유지 비용 적음
🔒 비정상 종료 감지 필요 LWT(Last Will) 기능으로 장애 탐지 가능
📊 데이터 수집이 잦은 스마트 팩토리, 홈 IoT 수백 대 장치를 효율적으로 관리 가능

예: 스마트홈, 공장 자동화, 드론 통신, 환경 모니터링 센서, 웨어러블 기기

✅ HTTP가 적합한 상황

상황 이유
🌐 웹 기반 애플리케이션 브라우저에서 HTTP/HTTPS 요청이 기본
📄 대량의 정적/동적 데이터 요청 이미지, 문서, JSON API 등에 적합
⏳ 간헐적인 데이터 통신 매번 새로 요청할 때 유리 (예: 하루에 한 번, 버튼 클릭 시 등)
🔒 표준 보안 요구가 명확한 시스템 HTTPS는 이미 대부분 보안이 내장됨
🧩 REST API 설계 및 문서화가 중요한 경우 Swagger, Postman 등 도구와 호환성 우수
📡 서버 주도적 구조 (서버에서 응답 위주) 클라이언트가 요청할 때만 응답하면 되는 구조에 적합

예: 사용자 로그인, 데이터베이스 질의, 웹페이지 요청, RESTful API 통신

📌실습 1 초음파 센서 실습

image
  • Trig 핀: 초음파 신호 송신 (Output)
  • Echo 핀: 반사된 초음파 수신 (Input)
  • 거리 계산: Echo 핀으로 들어오는 신호 시간을 측정하여 거리 계산

🔹실습 코드

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초 대기
}

🔹실습 결과

image

📌실습 2 초음파 센서 데이터 MQTT로 Publish 실습

  • 라이브러리 설치
image image
  • host에 코드에 설정한 test.mosquitto.org 입력
image
  • 검색 창에 user1/esp32/ultra을 입력하여 경로 지정 후 확인
image

🔹실습 코드

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

// 다음 변수들을 당신의 SSID와 비밀번호로 대체하세요.
const char* ssid = "wifi-SSID";             // 연결할 Wi-Fi의 SSID를 입력합니다.
const char* password = "wifi-Password";     // Wi-Fi의 비밀번호를 입력합니다.

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;
}

🔹실습 결과

IMG_7331.MOV

📌실습 3 능동 부저 제어 (Subscriber 만들기)

image

🔹실습 코드

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 초 기다리기
}

🔹실습 결과

IMG_7332.MOV

📌실습 4

  • host에 코드에 설정한 test.mosquitto.org 입력
  • user1/esp32/buzzer 토픽 사용
image image

🔹실습 코드

#include <WiFi.h>
#include <PubSubClient.h>
 
// 다음 변수들을 당신의 SSID와 비밀번호로 대체하세요.
const char* ssid = "wifi-SSID";             // 연결할 Wi-Fi의 SSID를 입력합니다.
const char* password = "wifi-Password";     // Wi-Fi의 비밀번호를 입력합니다.

// 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);
    }
  }
}

🔹실습 결과

  • 0 입력시 정지
  • 1 입력시 재생
default.MOV

✅ 주의!!

  • MQTT Explorer를 사용할때 맥은 앱스토어에서 다운로드함
  • MQTT Explorer에서 연결한 후 검색에 입력하지 않으면 프로그램이 버벅댐 (윈도우와 차이가 있을 수 있음)
⚠️ **GitHub.com Fallback** ⚠️