라즈베리파이에서 MQTT 프로토콜 활용 > 스마트기기시스템

본문 바로가기

[실습] 라즈베리파이에서 MQTT 프로토콜 활용

필기자
2025-05-27 15:20 1,091 0

본문

라즈베리파이에서 MQTT 프로토콜 활용

목적
  • MQTT 프로토콜에 대해 알아본다.
  • 라즈베리파이에서 MQTT 프로토콜을 활용한다.

목차
1. MQTT 프로토콜
2. MQTT 프로토콜을 활용 실습

1. MQTT 프로토콜
  • MQTT 프로토콜 개요
    • MQTT(Message Queuing Telemetry Transport)는 경량 메시지 전송 프로토콜
    • 제한된 대역폭·저전력 환경에서도 효율적인 통신을 위해 설계
 
  • MQTT 통신 계층 구조
20250527162606_82c60616dd5cfce502e4461870e3e64e_2eci.png
 
  • MQTT 메시지 캡슐화 과정
20250527181150_82c60616dd5cfce502e4461870e3e64e_0iok.png
  • MQTT 메세지(프로토콜) 구조
20250527182720_82c60616dd5cfce502e4461870e3e64e_1tve.png
  • Variable Header & Payload 실제 데이터 예시
20250527183016_82c60616dd5cfce502e4461870e3e64e_onpf.png
  • PUBLISH 메시지 바이트 구조 예시
20250527183131_82c60616dd5cfce502e4461870e3e64e_j3j3.png
  • MQTT 메세지 타입
    • Fixed Header 상위 4비트 값
    • 1~14 타입(예 0001 : CONNET, 0011 : PUBLISH) 
20250527183507_82c60616dd5cfce502e4461870e3e64e_8gp2.png
  • MQTT QoS(2~1비트)
20250527183841_82c60616dd5cfce502e4461870e3e64e_krty.png
  • MQTT 시스템 구성도 역할
20250527175547_82c60616dd5cfce502e4461870e3e64e_cs23.png
구성 요소 구분 주요 역할 구독/발행 토픽 예시
라즈베리파이 Publisher 센서 데이터 수집 후 MQTT 메시지 발행 home/temperature, home/humidity
MQTT Broker 중개 서버 메시지 전달 중계, 토픽 기반 라우팅 포트: 1883 사용
모바일 앱 Subscriber 실시간 모니터링 home/+
하위 1단계 토픽 모두 구독
웹 대시보드 Subscriber 시각화 및 대시보드 제공 home/#
하위 모든 하위 토픽 재귀적 구독
데이터베이스 Subscriber 센서 데이터 저장 home/temperature, home/humidity
 
  • HTTP / WebSocket / MQTT 비교
항목 HTTP WebSocket MQTT
통신 구조 요청-응답 (Client → Server) 양방향 (Client ↔ Server) 퍼블리셔-서브스크라이버 (중앙 브로커 기반)
Publisher-Subscriber (Broker)
연결 방식 단일 요청마다 연결 (비연속 연결) HTTP 최초 연결 후 지속 연결 (연결 유지)
WebSocket 별도 프로토콜 전환
지속 연결 (TCP 기반, KeepAlive 사용)
헤더 크기 큼 (수십~수백 바이트 이상) 초기 HTTP 핸드셰이크 후 간결 매우 작음 (2바이트부터 시작)
전송 방식 텍스트 기반 (JSON, HTML 등) 바이너리 또는 텍스트 프레임 바이너리 메시지 기반 (주로 센서 데이터)
QoS (신뢰성) 없음 (전송 실패 시 재요청 필요) 기본 TCP 신뢰성 3단계 QoS (0:최소, 1:1회, 2:정확히 1회)
실시간성 낮음 (요청마다 오버헤드 발생) 높음 (지속 연결 + 이벤트 기반 수신) 매우 높음 (경량 + 지속 연결 + 이벤트 기반)
방화벽/프록시 친화적 (80/443 포트 사용) 대부분 가능 (HTTP 핸드셰이크 기반) 제한 있음 (1883, 8883 포트 차단될 수 있음)
보안 HTTPS 사용 (TLS over 443) WSS 사용 가능 (WebSocket over TLS) TLS 가능 (MQTTS, 포트 8883)
오버헤드 큼 (헤더 + 매 요청 연결/종료) 중간 (초기만 큼, 이후 작음) 작음 (헤더 최소, 고정 연결)
확장성 좋음 (전 세계 웹 호환) 제한적 (서버 처리량 제약 존재) 좋음 (브로커 확장 가능, 클러스터링)
적용 분야 웹 API, RESTful 서비스, 게시판 등 실시간 채팅, 온라인 게임, 스트리밍 등 IoT 센서 네트워크, 스마트홈, 저전력 장치
의존성 구조 클라이언트-서버 직접 클라이언트-서버 직접 브로커를 통한 중개 통신
대표 포트 80 (HTTP), 443 (HTTPS) 80 (WS), 443 (WSS) 1883 (MQTT), 8883 (MQTTS)
 
20250527181611_82c60616dd5cfce502e4461870e3e64e_b2gf.png
  • HTTP Handshake
    • SYN : Synchronize
    • SYN-ACK : Synchronize - Acknowledgment
    • TLS : Transport Layer Security
20250527161049_82c60616dd5cfce502e4461870e3e64e_n860.png
20250527161251_82c60616dd5cfce502e4461870e3e64e_522u.png
20250527161420_82c60616dd5cfce502e4461870e3e64e_6m63.png
20250527161518_82c60616dd5cfce502e4461870e3e64e_xsjm.png
20250527161842_82c60616dd5cfce502e4461870e3e64e_liy0.png
 
  • KeepAlive : “연결이 살아있다”는 것을 확인하기 위해 주기적으로 소량의 패킷(핑)을 보내는 기능
    • 1. 클라이언트 → 브로커: PINGREQ (연결 확인 요청[0xC0 0x00, 192 0])20250527160033_82c60616dd5cfce502e4461870e3e64e_tfgw.png
    • 2. 브로커 → 클라이언트: PINGRESP (연결 확인 응답[0xD0 0x00, 208 0])20250527155955_82c60616dd5cfce502e4461870e3e64e_fsri.png 
    • 3. 설정된 시간 간격으로 반복 실행(예, 60초)

2. MQTT 프로토콜을 활용 실습
  • 시스템 구성도
20250527185743_82c60616dd5cfce502e4461870e3e64e_j37l.png
  • Mosquitto 브로커 설치
    • MQTT 프로토콜을 구현한 오픈소스 메시지 브로커
    • 특징
      • 경량(Lightweight): 라즈베리파이 등 저사양 환경에서도 사용 가능
      • QoS 지원: 메시지 전송 신뢰도 설정 가능 (QoS 0, 1, 2)
      • Pub/Sub 구조: 퍼블리셔-서브스크라이버 간 직접 통신 없음
        • 센서(라즈베리파이)→ 브로커(라즈베리파이) → 구독자(PC)
      • 와일드카드: +, #로 토픽 구독 범위 지정
        • + : 한 단계 토픽 대체 (예: home/+/temperature)
        • # : 여러 단계 포함 (예: home/#)
      • 다양한 언어 클라이언트 지원: Python, C, Java 등
      • TLS/암호화 지원: 보안 통신 가능 (옵션)
    • 포트
      • 1883: 기본 MQTT 포트 (비암호화)
      • 8883: TLS 암호화 MQTT


sudo apt update
sudo apt install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto
sudo systemctl start mosquitto

  • MQTT 외부 접속 허용


sudo nano /etc/mosquitto/mosquitto.conf

#제일 하단에 다음 추가
# 외부 접속 허용
listener 1883 0.0.0.0
allow_anonymous true

#저장후 MQTT브로커 재시작
sudo systemctl restart mosquitto

  • socket.io 서버 프로그램 수정
    • paho-mqtt 라이브러리 설치
    • iot_socket.py 파일 수정

(iot) pi@pi20221234:~/iot $ pip install paho-mqtt


import time
import threading
from flask import Flask, request
from flask_socketio import SocketIO
import RPi.GPIO as GPIO
import board
import adafruit_dht
import paho.mqtt.client as mqtt  #추가

# --- MQTT 클라이언트 설정 ---
def on_connect(client, userdata, flags, rc):
    print("MQTT 연결됨:", rc)  # MQTT 브로커에 연결되면 호출되는 콜백 함수

mqtt_client = mqtt.Client(protocol=mqtt.MQTTv311)      # MQTT 클라이언트 객체 생성 (v3.1.1 프로토콜 명시)
mqtt_client.on_connect = on_connect                    # 연결 콜백 등록 → 경고 방지 및 상태 확인용
mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)  # 재연결 지연 시간 설정 (최소 1초, 최대 30초)
mqtt_client.connect_async("localhost", 1883, 60)       # 비동기 방식으로 Mosquitto 브로커에 연결 시도
mqtt_client.loop_start()                               # 내부 네트워크 루프 실행 (백그라운드 스레드에서 처리)

# --- 글로벌 변수 ---
latest_temp = None
latest_hum = None

# --- GPIO 설정 ---
LED_PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)

# --- Flask-SocketIO 설정 ---
app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*", async_mode="eventlet")

# --- DHT22 센서 설정 ---
dht_device = adafruit_dht.DHT22(board.D18)

# --- LED 상태 반환 함수 ---
def get_led_state():
    return "on" if GPIO.input(LED_PIN) else "off"

# --- LED 제어 처리 ---
@socketio.on("led_control")
def control_led(data):
    state = data.get("state")
    if state == "on":
        GPIO.output(LED_PIN, GPIO.HIGH)
    elif state == "off":
        GPIO.output(LED_PIN, GPIO.LOW)
    state = get_led_state()
    print(f"led 상태 : {state}")
    socketio.emit("led_status", {"state": state})

# --- LED 상태 요청 처리 ---
@socketio.on("get_led_status")
def handle_status_request():
    state = get_led_state()
    print(f"led 상태 : {state}")
    socketio.emit("led_status", {"state": state}, room=request.sid)

# --- 온습도 상태 요청 처리 ---
@socketio.on("get_temperature_humidity_status")
def send_temperature_humidity_status():
    ret_temp_hum = {"temp": "N/A", "hum": "N/A"}
    if latest_temp is not None and latest_hum is not None:
        ret_temp_hum = {"temp": latest_temp, "hum": latest_hum}
    socketio.emit("temperature_humidity_status", ret_temp_hum, room=request.sid)

# --- 센서 측정 스레드 ---
def temperature_monitor_thread():
    global latest_temp, latest_hum
    while True:
        try:
            temp = dht_device.temperature
            hum = dht_device.humidity
            if hum is not None and temp is not None:
                latest_temp = round(temp, 1)
                latest_hum = round(hum, 1)
                print(f"센서 측정: {latest_temp}℃ / {latest_hum}%")

                # MQTT 퍼블리시
                #mqtt_client.publish("home/temperature", str(latest_temp), qos=1)
                #mqtt_client.publish("home/humidity", str(latest_hum), qos=1)
                mqtt_client.publish("home/temperature", str(latest_temp))
                mqtt_client.publish("home/humidity", str(latest_hum))

            else:
                print("센서 데이터 없음")
        except RuntimeError as e:
            print("센서 에러:", e.args[0])
        except Exception as e:
            dht_device.exit()
            raise e
        time.sleep(2)

# --- 센서 스레드 시작 ---
def start_sensor_thread():
    t = threading.Thread(target=temperature_monitor_thread)
    t.daemon = True
    t.start()

# --- 메인 ---
if __name__ == "__main__":
    print("서버 시작")
    start_sensor_thread()
    socketio.run(app, host="0.0.0.0", port=5000)

  • mqtt_client.publish() 주요 인자
    • 사용법 : mqtt_client.publish("sensor/temp", "25.4", qos=1, retain=True)
인자명 자료형 설명
topic str 퍼블리시할 토픽 경로
payload str or bytes 전송할 메시지 내용
qos int QoS 레벨 (0, 1, 2)
retain bool True이면 브로커가 마지막 메시지를 저장 (기본값: False)
properties Properties MQTT 5.0 속성 (선택사항)
 
  • 라즈베리파이 내에서 MQTT 클라이언트 구독

mosquitto_sub -h localhost -t "home/#" -v

20250528122825_80a2883f94ae73c041031c15279ed809_8yq0.png
  • 윈도우PC 에서 구독
20250527193927_82c60616dd5cfce502e4461870e3e64e_ghgx.png
20250528122627_80a2883f94ae73c041031c15279ed809_iiwr.png

댓글목록0

등록된 댓글이 없습니다.
게시판 전체검색