Zephyr ESP32 실습 #6 – MQTT 통신

서론

이전 실습에서는 ESP32의 Wi-Fi 기능을 이용하여 UDP와 TCP 통신을 구현하였습니다. 특히 TCP 실습에서는 ESP32와 PC가 직접 연결되어 데이터를 송수신하는 과정을 통해 소켓 기반 네트워크 통신의 기본 구조를 확인하였습니다.

이번 실습에서는 여기서 한 단계 더 나아가 IoT 시스템에서 널리 사용되는 MQTT(Message Queuing Telemetry Transport) 통신을 구현합니다.

MQTT는 TCP를 기반으로 동작하지만, 장치가 서로 직접 연결되는 대신 MQTT Broker를 중심으로 메시지를 주고받습니다. 송신자는 특정 Topic으로 메시지를 Publish하고, 해당 Topic을 Subscribe한 장치는 Broker를 통해 메시지를 전달받습니다.

이번 실습에서는 이전에 구축한 Orange Pi One의 Mosquitto MQTT Broker를 이용합니다.

아래는 해당 글의 링크 입니다.

Orange Pi IoT 서버 #1 – Linux 기반 Mosquitto MQTT Broker와 FastAPI 구축

ESP32에는 Zephyr의 MQTT Client를 구현하여 Broker에 접속하고 메시지를 송수신합니다. 또한 Orange Pi에서 실행되는 FastAPI와 MQTT를 연결하여 다음과 같은 전체 통신 구조도 확인합니다.

ESP32 ↔ Mosquitto MQTT Broker ↔ FastAPI

이를 통해 UDP와 TCP에서 살펴본 직접적인 네트워크 통신에서 한 단계 확장하여, Broker를 중심으로 여러 장치와 서버가 데이터를 교환하는 MQTT 기반 IoT 통신 구조를 살펴보겠습니다.

MQTT란?

MQTT(Message Queuing Telemetry Transport)는 IoT 환경에서 장치 간 데이터를 효율적으로 전달하기 위해 널리 사용되는 메시지 통신 프로토콜입니다. TCP/IP를 기반으로 동작하며 비교적 구조가 단순하고 통신에 필요한 데이터의 양이 적어 ESP32와 같은 임베디드 장치에서도 많이 사용됩니다.

MQTT의 가장 큰 특징은 TCP 통신처럼 두 장치가 서로 직접 데이터를 주고받는 것이 아니라, 중간에 Broker가 존재한다는 것입니다.

MQTT 통신에는 다음과 같은 세 가지 개념이 중요합니다.

  • Broker : MQTT 메시지를 중계하는 서버입니다.
  • Publisher : 특정 Topic으로 메시지를 전송(Publish)합니다.
  • Subscriber : 특정 Topic을 구독(Subscribe)하고 해당 Topic으로 전달되는 메시지를 수신합니다.

아래는 MQTT 로 구현한 온도센싱의 예에 대한 그림입니다.

위 그림의 출처는 다음과 같습니다.

Getting Started with MQTT — Part 1

그림에서 온도 센서는 temp라는 Topic으로 온도 데이터를 Publish합니다. Broker는 이 메시지를 받아 temp Topic을 Subscribe하고 있는 컴퓨터와 모바일 장치에 전달합니다.

여기서 Topic은 MQTT 메시지를 구분하기 위한 일종의 주소 또는 채널이라고 볼 수 있습니다. Publisher는 데이터를 받을 장치를 직접 지정하지 않고 Topic만 지정하여 메시지를 전송합니다. Subscriber 역시 데이터를 보내는 장치를 직접 지정하지 않고 자신이 필요한 Topic을 구독합니다.

이러한 방식을 Publish/Subscribe(Pub/Sub) 구조​라고 합니다. 송신 측과 수신 측이 서로 직접 연결될 필요가 없기 때문에 새로운 장치를 추가하거나 여러 장치가 동일한 데이터를 공유하기가 쉽습니다.

또한 하나의 Publisher가 보낸 메시지를 여러 Subscriber가 동시에 받을 수 있으며, 하나의 장치가 여러 Topic을 Publish하거나 Subscribe하는 것도 가능합니다. 이러한 구조는 센서와 제어 장치가 다수 연결되는 IoT 시스템을 구성하는 데 유리합니다.

MQTT는 이처럼 Broker와 Topic을 중심으로 송신자와 수신자를 분리하는 구조를 사용한다는 점에서 일반적인 TCP 소켓 통신과 차이가 있습니다.

MQTT 실습구성

이번 실습에서는 ESP32, Orange Pi One, PC​를 이용하여 MQTT 통신 환경을 구성합니다.

실습 구성은 아래와 같습니다.

ESP32는 Zephyr의 MQTT Client로 동작하며 Wi-Fi를 통해 Orange Pi One에 설치된 Mosquitto MQTT Broker에 접속합니다. ESP32는 특정 Topic으로 메시지를 Publish하고, 다른 Topic을 Subscribe하여 Broker로부터 메시지를 수신합니다.

Orange Pi One에서는 Mosquitto와 FastAPI가 함께 실행됩니다. Mosquitto는 MQTT 메시지를 중계하는 Broker 역할을 하며, FastAPI의 main.py 역시 MQTT Client로 Broker에 연결됩니다.

따라서 ESP32와 FastAPI가 직접 연결되는 것이 아니라 다음과 같이 Mosquitto를 중심으로 메시지를 주고받는 구조가 됩니다.

ESP32 (Zephyr) ↔ Mosquitto MQTT Broker ↔ FastAPI

FastAPI는 MQTT로 수신한 데이터를 저장하여 브라우저에서 확인할 수 있도록 하고, 반대로 Web API를 통해 입력된 명령을 MQTT로 Publish할 수도 있습니다.

각 장치의 역할을 정리하면 다음과 같습니다.

  • ESP32 (Zephyr) : MQTT Client로 동작하며 메시지를 Publish/Subscribe합니다.
  • Mosquitto (Orange Pi One) : MQTT Broker로 동작하며 Publisher와 Subscriber 사이의 메시지를 중계합니다.
  • FastAPI (Orange Pi One) : MQTT Client로 Mosquitto와 통신하면서 동시에 Web API를 제공합니다.
  • PC Web Browser : FastAPI에 접속하여 MQTT를 통해 전달된 데이터를 확인하거나 명령을 전달합니다.

이번 실습에서는 이러한 구성을 이용하여 ESP32 ↔ Mosquitto ↔ FastAPI 사이의 양방향 메시지 전달을 확인합니다.

이를 통해 ESP32가 단순히 다른 장치와 직접 통신하는 구조에서 벗어나, MQTT Broker를 중심으로 여러 장치와 서버가 연결될 수 있는 기본적인 IoT 통신 구조를 구현해 보겠습니다.

MQTT 프로젝트 생성

이번 MQTT 프로젝트는 이전에 구현한 Wi-Fi TCP 통신 프로젝트를 기반으로 생성합니다.

TCP 실습에서 이미 ESP32의 Wi-Fi 연결과 DHCP를 이용한 IP 주소 할당까지 구현하였으므로, 이 부분을 그대로 활용하고 TCP 통신 부분을 MQTT 통신으로 변경하는 방식으로 진행합니다.

기존 프로젝트 폴더를 복사한 후 프로젝트 이름을 다음과 같이 변경합니다.

skpang_esp32_can_wifimqtt

프로젝트의 기본 구성은 이전 TCP 프로젝트와 동일하며, 사용하는 보드 역시 기존에 제작한 skpang_esp32_can 커스텀 보드를 그대로 사용합니다.

아래는 이번 프로젝트의 폴더 구성입니다.

src 폴더에 있는 파일들 중 tcp_client.c 와 tcp_client.h 파일은 이름을 mqtt_client.c 와 mqtt_client.h로 변경합니다.

내용은 아직 변경하지 않습니다. 진행 하면서 하나씩 변경하도록 하겠습니다.

우선 기본 구성 파일 들인 CMakeLists.txt, prj.conf 그리고 CHANGELOG.md 파일을 우리 프로젝트에 맞게 변경 하겠습니다.

아래는 CMakeLists.txt 파일 입니다.

프로젝트 이름을 skpang_esp32_can_wifimqtt로 변경하였고 target_sources 부분도 src/main.c와 src/mqtt_client.c 로 바꾸었습니다.

다음은 prj.conf 파일입니다.

TCP에서 작성 되었던 것에서 MQTT 부분이 추가 되었습니다.

다음은 CHANGELOG.md입니다.

여기서도 기존 파일에 MQTT 부분을 추가 하였습니다.

처음엔 Start로 기록 했다가 프로젝트를 진행하면서 하나씩 채워 나가는 방식을 추천 합니다. 물론 종료 후에 한꺼번에 정리를 해도 됩니다.

MQTT 프로그램 구현

개요

프로젝트의 기본 설정이 완료되었으므로 이제 ESP32에서 동작하는 MQTT 프로그램을 구현하겠습니다.

이번 프로젝트에서는 프로그램의 기능을 크게 Wi-Fi 연결 부분과 MQTT 통신 부분으로 나누었습니다.

Wi-Fi 연결은 이전 TCP 프로젝트에서 구현한 코드를 그대로 활용하고, MQTT와 관련된 기능은 별도의 mqtt_client.c 파일에서 처리하도록 구성하였습니다.

전체적인 프로그램의 동작 순서는 다음과 같습니다.

  1. ESP32의 Wi-Fi를 초기화합니다.
  2. 설정된 AP에 연결합니다.
  3. DHCP를 통해 IP 주소를 할당받습니다.
  4. Mosquitto MQTT Broker에 연결합니다.
  5. 지정된 Topic을 Subscribe합니다.
  6. ESP32의 데이터를 주기적으로 Publish합니다.
  7. Broker로부터 수신되는 MQTT 메시지를 처리합니다.

즉, 이전 TCP 실습에서는 소켓을 직접 생성하고 서버에 연결한 후 데이터를 송수신했지만, 이번에는 Zephyr의 MQTT API를 이용하여 Broker 연결과 메시지 송수신을 처리합니다.

프로그램은 주요 기능에 따라 다음 두 파일로 나누어 살펴보겠습니다.

  • main.c : Wi-Fi 연결과 MQTT Client 실행
  • mqtt_client.c : MQTT Broker 연결 및 Publish/Subscribe 처리

먼저 main.c부터 살펴보겠습니다.

main.c

main.c는 ESP32의 Wi-Fi 연결을 초기화하고, IP 주소가 할당된 후 MQTT Client를 실행하는 역할​을 합니다.

Wi-Fi와 관련된 부분은 이전 TCP 프로젝트에서 사용한 코드를 대부분 그대로 사용하므로 여기서는 다시 자세히 설명하지 않고, MQTT 프로그램과 연결되는 부분을 중심으로 살펴보겠습니다.

먼저 main.c에서는 MQTT Client를 사용하기 위해 다음 헤더 파일을 추가합니다.

#include "mqtt_client.h"

ESP32가 AP에 연결되고 DHCP를 통해 IP 주소를 정상적으로 할당받으면 MQTT Client를 시작합니다.

Ipv4 address event handler 인 ipv4_addr_handler() 함수에서 MQTT Client 를 시작 시키는 함수가 있습니다.

함수 이름은 아래와 같습니다.

mqtt_app_start();

MQTT Broker에 접속하려면 먼저 네트워크 통신이 가능한 상태가 되어야 하므로, DHCP를 통해 IPv4 주소가 정상적으로 할당된 것을 확인한 후 mqtt_app_start()를 호출합니다.

아래는 MQTT Client를 시작시키는 함수가 포함된 handler 함수의 코드 입니다.

Wi-Fi 연결과 관련된 main.c의 기본 코드는 이전 글인  Zephyr ESP32 실습 #5 – Wi-Fi TCP 통신에서 확인할 수 있습니다. 이번 프로젝트에서는 여기에 MQTT Client를 시작하는 부분을 추가하였습니다.

전체적인 실행 흐름은 다음과 같습니다.

ESP32 시작 → Wi-Fi 초기화 → AP 연결 → IP 주소 할당 → MQTT Client 시작

main.c는 네트워크 연결 환경을 준비하고, 실제 MQTT Broker 접속이나 Publish/Subscribe 처리는 mqtt_client.c에 맡기는 구조입니다.

이렇게 Wi-Fi 연결 부분과 MQTT 기능을 분리하면 main.c가 지나치게 길어지는 것을 방지할 수 있고, MQTT 관련 기능도 별도의 파일에서 관리할 수 있습니다.

다음으로 실제 MQTT 통신의 핵심 기능을 담당하는 mqtt_client.c를 살펴보겠습니다.

mqtt_client.c

mqtt_client.c는 ESP32의 MQTT 통신 기능을 담당합니다.

이 파일에서는 MQTT Broker의 주소와 Port를 설정하고, MQTT Client를 초기화한 후 Broker에 연결합니다. 연결이 완료되면 esp32/control Topic을 Subscribe하고, esp32/test Topic으로 1초마다 숫자를 Publish합니다.

또한 Broker로부터 메시지가 수신되면 MQTT Event Handler에서 Topic과 Payload를 읽어 출력합니다.

주요 동작 흐름은 다음과 같습니다.

Broker 설정 → MQTT Client 초기화 → Broker 연결 → Subscribe → Publish / 수신 처리 → 연결 유지

코드는 기능에 따라 다음과 같이 나누어 살펴보겠습니다.

  • Broker와 MQTT Client 기본 설정
  • MQTT Event Handler
  • Broker 초기화
  • MQTT Client 초기화
  • MQTT Broker 연결 및 처리 Loop
  • MQTT Publish
  • MQTT Subscribe

Broker와 MQTT Client 기본 설정

아래는 Briker와 MQTT Client 기본 설정에 대한 코드 입니다.

먼저 MQTT 통신에 필요한 Broker 정보와 Client ID를 정의합니다.

#define MQTT_BROKER_IP      "192.168.0.7"
#define MQTT_BROKER_PORT    1883
#define MQTT_CLIENT_ID      "esp32_zephyr"

MQTT_BROKER_IPMosquitto MQTT Broker가 실행되고 있는 Orange Pi One의 IP 주소입니다.

MQTT_BROKER_PORT는 MQTT 통신에 사용할 Port 번호이며, 여기서는 Mosquitto의 기본 MQTT Port인 1883을 사용합니다.

MQTT_CLIENT_ID는 MQTT Broker에 접속하는 Client를 구분하기 위한 이름입니다. 이번 실습에서는 ESP32의 Client ID를 esp32_zephyr로 설정하였습니다.

다음은 MQTT Client와 Broker의 정보를 저장하기 위한 변수입니다.

static struct mqtt_client client;
static struct sockaddr_storage broker;

client는 Zephyr에서 제공하는 struct mqtt_client 구조체로, MQTT Client의 설정과 현재 통신 상태에 필요한 정보를 관리합니다.

broker에는 접속할 MQTT Broker의 IP 주소와 Port 등의 네트워크 주소 정보가 저장됩니다.

MQTT 통신에서 사용할 송수신 Buffer도 준비합니다.

static uint8_t rx_buffer[256];
static uint8_t tx_buffer[256];

rx_buffer는 Broker에서 들어오는 MQTT 데이터를 처리하는 데 사용하고, tx_buffer는 Broker로 전송할 MQTT 데이터를 처리하는 데 사용합니다. 이번 실습에서는 각각 256 Byte의 Buffer를 사용하였습니다.

마지막으로 Broker와의 연결 상태를 확인하기 위한 변수를 선언합니다.

static bool mqtt_connected;

이 변수는 MQTT Broker와의 연결이 정상적으로 완료되면 true로 변경됩니다. 이후 프로그램에서는 이 값을 확인하여 Broker 연결이 완료된 상태에서 메시지를 Publish하도록 합니다.

또한 아래 두 함수는 뒤에서 구현되므로 먼저 함수 원형을 선언해 둡니다.

static int mqtt_publish_message(const char *topic, const char *payload);
static int mqtt_subscribe_topic(void);

mqtt_publish_message()는 지정한 Topic으로 메시지를 Publish하고, mqtt_subscribe_topic()은 ESP32가 수신할 Topic을 Subscribe하는 함수입니다.

이렇게 MQTT 통신에 필요한 기본 정보를 준비한 후, 다음으로 MQTT Event Handler를 구현합니다.

MQTT Event Handler

MQTT Client의 연결 상태가 변하거나 메시지가 수신되면 Zephyr의 MQTT 라이브러리는 Event를 발생시킵니다.

이번 프로그램에서는 이러한 Event를 mqtt_evt_handler() 함수에서 처리합니다.

아래는 mqtt_evt_handler()함수의 코드 입니다.

evt->type에는 발생한 MQTT Event의 종류가 전달되며, switch 문에서 각 Event에 맞는 동작을 수행합니다.

MQTT_EVT_CONNACK

MQTT_EVT_CONNACK는 ESP32가 MQTT Broker에 연결을 요청한 후, Broker로부터 연결 응답을 받았을 때 발생합니다.

evt->result0이면 Broker와의 연결이 정상적으로 완료된 것입니다.

연결이 완료되면 mqtt_connectedtrue로 변경하고, 이어서 mqtt_subscribe_topic()을 호출하여 esp32/control Topic을 Subscribe합니다.

즉 MQTT 연결이 완료된 직후 ESP32가 제어 메시지를 받을 준비를 하도록 구성한 것입니다.

MQTT_EVT_PUBLISH

MQTT_EVT_PUBLISH는 ESP32가 Subscribe한 Topic으로 메시지가 전달되었을 때 발생합니다.

먼저 evt->param.publish에서 수신된 메시지 정보를 가져옵니다.

p->message.topic.topic에는 수신된 Topic 정보가 들어 있으므로 printk()를 이용하여 Topic 이름을 출력합니다.

실제 메시지 내용인 Payload는 다음 함수로 읽습니다.

mqtt_read_publish_payload()

읽은 데이터의 마지막에 '\0'을 추가한 후 문자열로 출력합니다.

예를 들어 esp32/control Topic으로 ON이라는 메시지를 Publish하면 ESP32에서는 다음과 같이 확인할 수 있습니다.

MQTT message received
Topic: esp32/control
Payload: ON

따라서 이 Event가 Broker에서 ESP32로 전달되는 MQTT 메시지를 처리하는 핵심 부분입니다.

MQTT_EVT_DISCONNECT

Broker와의 MQTT 연결이 종료되면 MQTT_EVT_DISCONNECT Event가 발생합니다.

현재 프로그램에서는 연결 종료 사실과 결과 값만 출력합니다.

이처럼 mqtt_evt_handler()는 Broker 연결 완료, 메시지 수신, 연결 종료와 같은 MQTT의 주요 상태 변화를 처리하는 Callback 함수입니다.

다음으로 실제 Broker의 IP 주소와 Port를 설정하는 broker_init() 함수를 살펴보겠습니다.

Broker 초기화

다음 broker_init() 함수에서는 ESP32가 접속할 MQTT Broker의 네트워크 주소를 설정합니다.

먼저 앞에서 선언한 broker를 IPv4 주소를 저장하는 sockaddr_in 구조체로 사용합니다.

struct sockaddr_in *broker4 = (struct sockaddr_in *)&broker;

그리고 Broker와 통신하기 위한 주소 체계와 Port를 설정합니다.

broker4->sin_family = AF_INET;
broker4->sin_port = htons(MQTT_BROKER_PORT);

AF_INETIPv4를 사용한다는 의미이며, MQTT_BROKER_PORT에는 앞에서 정의한 1883이 사용됩니다.

htons()는 Port 번호를 네트워크에서 사용하는 Byte 순서(Network Byte Order)로 변환합니다.

다음으로 문자열 형태로 정의된 Broker의 IP 주소를 실제 네트워크 주소 형식으로 변환합니다.

zsock_inet_pton(AF_INET, MQTT_BROKER_IP,
                &broker4->sin_addr)

이번 프로그램에서 MQTT_BROKER_IP는 다음과 같이 정의되어 있습니다.

#define MQTT_BROKER_IP "192.168.0.7"

따라서 zsock_inet_pton()"192.168.0.7"이라는 문자열을 IPv4 주소 형식으로 변환하여 broker4->sin_addr에 저장합니다.

변환에 실패하면 오류 메시지를 출력하고 -1을 반환합니다.

결국 broker_init()의 역할은 Mosquitto MQTT Broker의 IP 주소와 Port를 Zephyr의 MQTT Client가 사용할 수 있는 네트워크 주소 형태로 준비하는 것입니다.

다음으로 이 Broker 정보를 이용하여 Zephyr MQTT Client 자체를 설정하는 client_init() 함수를 살펴보겠습니다.

MQTT Client 초기화

client_init() 함수에서는 Zephyr의 MQTT Client를 초기화하고 Broker 연결에 필요한 여러 가지 정보를 설정합니다.

먼저 mqtt_client_init()을 호출하여 MQTT Client 구조체를 기본값으로 초기화합니다.

mqtt_client_init(&client);

그다음 앞의 broker_init()에서 설정한 Broker 주소와 MQTT Event Handler를 등록합니다.

client.broker = &broker;
client.evt_cb = mqtt_evt_handler;

client.broker에는 접속할 Broker의 주소가 지정되고, client.evt_cb에는 앞에서 살펴본 mqtt_evt_handler()가 등록됩니다.

따라서 Broker 연결이나 메시지 수신 등의 MQTT Event가 발생하면 mqtt_evt_handler()가 호출됩니다.

다음은 MQTT Client ID를 설정합니다.

client.client_id.utf8 = (uint8_t *)MQTT_CLIENT_ID;
client.client_id.size = strlen(MQTT_CLIENT_ID);

앞에서 MQTT_CLIENT_ID를 다음과 같이 정의하였습니다.

#define MQTT_CLIENT_ID "esp32_zephyr"

따라서 Broker에는 이 ESP32가 esp32_zephyr라는 Client ID로 접속하게 됩니다. MQTT Broker는 Client ID를 이용하여 연결된 MQTT Client를 구분합니다.

이번 실습에서는 별도의 사용자 인증을 사용하지 않으므로 사용자 이름과 비밀번호를 NULL로 설정합니다.

client.password = NULL;
client.user_name = NULL;

MQTT Protocol Version은 MQTT 3.1.1을 사용합니다.

client.protocol_version = MQTT_VERSION_3_1_1;

다음으로 MQTT 통신에 사용할 수신 및 송신 Buffer를 지정합니다.

client.rx_buf = rx_buffer;
client.rx_buf_size = sizeof(rx_buffer);

client.tx_buf = tx_buffer;
client.tx_buf_size = sizeof(tx_buffer);

앞에서 각각 256 Byte로 선언한 rx_buffertx_buffer를 MQTT Client의 송수신 Buffer로 사용하도록 설정합니다.

마지막으로 MQTT의 전송 방식을 설정합니다.

client.transport.type = MQTT_TRANSPORT_NON_SECURE;

MQTT_TRANSPORT_NON_SECURETLS를 사용하지 않는 일반 TCP 연결을 의미합니다. 따라서 이번 실습에서는 MQTT의 기본 Port인 1883을 이용하여 Broker와 통신합니다.

결국 client_init()Broker 주소, Event Callback, Client ID, Protocol Version, 송수신 Buffer, Transport 방식 등 MQTT Client가 Broker에 접속하기 위해 필요한 정보를 설정하는 함수​입니다.

이제 Broker와 MQTT Client의 설정이 모두 준비되었으므로 다음으로 mqtt_app_start()에서 실제 Broker 연결을 시작하고 MQTT 통신을 처리하는 과정을 살펴보겠습니다.

MQTT Broker 연결 및 처리 Loop

mqtt_app_start() 함수에서는 앞에서 준비한 Broker와 MQTT Client를 초기화하고, 실제 Mosquitto MQTT Broker에 연결합니다. 이후 반복 Loop에서 MQTT 메시지 수신, 연결 유지 및 주기적인 Publish를 처리합니다.

먼저 Broker와 MQTT Client를 초기화합니다.

ret = broker_init();
if (ret != 0) {
    return ret;
}

client_init();

broker_init()에서 Broker의 IP 주소와 Port를 설정하고, client_init()에서 MQTT Client에 필요한 정보를 설정합니다.

다음으로 mqtt_connect()를 호출하여 Broker에 연결을 요청합니다.

printk("Connecting to MQTT broker %s:%d...\n",
       MQTT_BROKER_IP, MQTT_BROKER_PORT);

ret = mqtt_connect(&client);
if (ret != 0) {
    printk("mqtt_connect failed: %d\n", ret);
    return ret;
}

여기서 중요한 점은 mqtt_connect()가 성공했다고 해서 앞에서 살펴본 mqtt_connected가 바로 true가 되는 것은 아니라는 것입니다.

Broker로부터 MQTT 연결 응답인 CONNACK을 수신하고, mqtt_input()을 통해 해당 패킷이 처리되면 MQTT_EVT_CONNACK Event가 발생합니다.

앞에서 작성한 Event Handler에서는 이때 다음과 같이 연결 상태를 변경하였습니다.

mqtt_connected = true;

그리고 mqtt_subscribe_topic()을 호출하여 esp32/control Topic을 Subscribe합니다.

따라서 연결 과정은 다음과 같이 이해할 수 있습니다.

mqtt_connect() → Broker 연결 요청 → CONNACK 수신 → MQTT_EVT_CONNACK → Subscribe

이후 프로그램은 while Loop에서 MQTT 통신을 계속 처리합니다.

while (1) {
    ret = mqtt_input(&client);

    if (ret != 0 && ret != -EAGAIN) {
        printk("mqtt_input error: %d\n", ret);
        break;
    }

    ret = mqtt_live(&client);

    if (ret != 0 && ret != -EAGAIN) {
        printk("mqtt_live error: %d\n", ret);
        break;
    }

    ...
}

mqtt_input()은 Broker로부터 들어오는 MQTT 패킷을 처리합니다. 앞에서 살펴본 MQTT_EVT_CONNACK이나 MQTT_EVT_PUBLISH와 같은 Event도 수신된 패킷이 이 함수를 통해 처리되면서 발생합니다.

mqtt_live()는 MQTT 연결의 Keep Alive를 관리합니다. 필요한 경우 Broker와의 연결이 유지될 수 있도록 MQTT의 연결 유지 처리를 수행합니다.

-EAGAIN은 현재 처리할 데이터가 없거나 즉시 처리를 완료할 수 없는 경우 발생할 수 있으므로 여기서는 오류로 처리하지 않고 Loop를 계속 실행합니다.

Broker와의 연결이 완료되면 ESP32는 1초마다 숫자를 Publish합니다.

if (mqtt_connected) {
    char payload[16];

    snprintf(payload, sizeof(payload), "%d", count);

    mqtt_publish_message("esp32/test", payload);

    count++;
}

k_sleep(K_SECONDS(1));

count 값을 문자열로 변환한 후 esp32/test Topic으로 Publish합니다. 따라서 Broker에는 다음과 같이 값이 전달됩니다.

esp32/test → 0
esp32/test → 1
esp32/test → 2
esp32/test → 3
...

이 숫자 자체에 특별한 의미가 있는 것은 아니며, 이번 실습에서 ESP32가 MQTT 메시지를 정상적으로 Publish하는지 확인하기 위한 Test Data입니다.

Loop가 오류 등으로 종료되면 마지막으로 Broker와의 연결을 종료합니다.

mqtt_disconnect(&client, NULL);

따라서 mqtt_app_start()MQTT Broker 연결을 시작하고, 수신 패킷과 Keep Alive를 처리하면서 주기적으로 Test Data를 Publish하는 MQTT 프로그램의 중심 함수​라고 할 수 있습니다.

아래는 mqtt_app_start() 함수의 코드 입니다.

다음으로 실제 esp32/test Topic에 메시지를 전송하는 mqtt_publish_message() 함수를 살펴보겠습니다.

MQTT 메시지 Publish

mqtt_publish_message() 함수는 지정한 Topic으로 MQTT 메시지를 Publish하는 역할을 합니다.

함수의 인자로 Topic과 Payload를 전달하도록 구성되어 있으므로, 동일한 함수를 이용하여 여러 Topic으로 서로 다른 데이터를 전송할 수 있습니다.

먼저 MQTT Publish에 필요한 정보를 저장하는 mqtt_publish_param 구조체를 선언하고 초기화합니다.

struct mqtt_publish_param param;

memset(&param, 0, sizeof(param));

다음으로 메시지를 Publish할 Topic과 QoS를 설정합니다.

param.message.topic.qos = MQTT_QOS_0_AT_MOST_ONCE;
param.message.topic.topic.utf8 = (uint8_t *)topic;
param.message.topic.topic.size = strlen(topic);

이번 실습에서는 QoS 0을 사용합니다.

MQTT_QOS_0_AT_MOST_ONCE는 메시지를 한 번 전송하며 수신 여부를 별도로 확인하지 않는 가장 간단한 전송 방식입니다. 따라서 통신 과정이 단순하고 추가적인 응답 처리가 필요하지 않습니다.

실제로 전송할 데이터는 Payload에 설정합니다.

param.message.payload.data = (uint8_t *)payload;
param.message.payload.len = strlen(payload);

MQTT 메시지는 크게 보면 어떤 Topic으로 보낼 것인지어떤 데이터를 보낼 것인지가 필요합니다.

예를 들어 다음과 같이 함수를 호출하면,

mqtt_publish_message("esp32/test", "10");

esp32/testTopic, "10"Payload가 됩니다.

그 밖의 Publish 관련 Parameter도 설정합니다.

param.message_id = 1;
param.dup_flag = 0;
param.retain_flag = 0;

dup_flag는 재전송 메시지 여부를 나타내며, retain_flag는 Broker가 해당 메시지를 Retain Message로 보관할 것인지를 지정합니다. 이번 실습에서는 모두 사용하지 않으므로 0으로 설정합니다.

설정이 완료되면 mqtt_publish()을 호출하여 실제 메시지를 Broker로 전송합니다.

ret = mqtt_publish(&client, &param);

전송에 성공하면 Topic과 Payload를 출력하여 전송된 내용을 확인합니다.

Published: esp32/test -> 0
Published: esp32/test -> 1
Published: esp32/test -> 2

따라서 이번 프로그램에서는 ESP32가 Publisher가 되고, esp32/test가 Publish Topic이 됩니다.

mqtt_publish_message()는 MQTT 메시지에 필요한 Topic, Payload, QoS 등의 정보를 구성한 후 Zephyr의 mqtt_publish() API를 이용하여 Broker로 메시지를 전달하는 함수입니다.

다음으로 ESP32가 esp32/control Topic의 메시지를 수신할 수 있도록 등록하는 mqtt_subscribe_topic() 함수를 살펴보겠습니다.

MQTT Topic Subscribe

mqtt_subscribe_topic() 함수는 ESP32가 Broker로부터 메시지를 받을 수 있도록 특정 Topic을 Subscribe하는 역할을 합니다.

이번 프로그램에서는 esp32/control Topic을 Subscribe합니다.

먼저 Subscribe할 Topic의 정보를 저장하기 위해 mqtt_topic 구조체를 선언합니다.

struct mqtt_topic sub_topic;

그리고 Subscribe할 Topic과 QoS를 설정합니다.

sub_topic.topic.utf8 = (uint8_t *)"esp32/control";
sub_topic.topic.size = strlen("esp32/control");
sub_topic.qos = MQTT_QOS_0_AT_MOST_ONCE;

따라서 ESP32는 esp32/control Topic을 QoS 0으로 Subscribe하게 됩니다.

다음으로 Subscribe할 Topic을 mqtt_subscription_list 구조체에 등록합니다.

sub_list.list = &sub_topic;
sub_list.list_count = 1;
sub_list.message_id = 1;

list에는 Subscribe할 Topic의 정보가 지정되고, list_count는 등록할 Topic의 개수를 나타냅니다. 이번에는 esp32/control 하나만 Subscribe하므로 1로 설정합니다.

설정이 완료되면 mqtt_subscribe()를 호출하여 Broker에 Subscribe를 요청합니다.

return mqtt_subscribe(&client, &sub_list);

이 함수는 앞에서 살펴본 MQTT_EVT_CONNACK Event가 발생했을 때 호출됩니다. 따라서 Broker와의 연결이 정상적으로 완료된 후 esp32/control Topic을 Subscribe하게 됩니다.

이후 다른 MQTT Client가 esp32/control Topic으로 메시지를 Publish하면 Broker는 해당 메시지를 ESP32에 전달합니다.

통신 흐름은 다음과 같습니다.

다른 MQTT Client → Publish (esp32/control) → Mosquitto Broker → ESP32

ESP32에 메시지가 도착하면 앞에서 살펴본 MQTT_EVT_PUBLISH Event가 발생하고, Event Handler에서 Topic과 Payload를 읽어 처리합니다.

따라서 이번 프로그램에서 ESP32는 두 가지 역할을 동시에 수행합니다.

Publish

esp32/test → 1초마다 Test Data 전송

Subscribe

esp32/control → 외부에서 전달되는 제어 메시지 수신

이것으로 mqtt_client.c의 주요 기능을 모두 살펴보았습니다. ESP32는 하나의 MQTT Client로서 Publisher와 Subscriber 역할을 동시에 수행하며, Mosquitto Broker를 통해 다른 MQTT Client와 양방향으로 메시지를 주고받을 수 있습니다.

FastAPI와 MQTT 연결

앞에서는 ESP32가 Mosquitto MQTT Broker에 접속하여 메시지를 Publish하고 Subscribe하는 과정을 살펴보았습니다.

이제 Orange Pi One에서 실행되는 FastAPI와 MQTT Broker를 연결하여 ESP32와 Web API 사이에서도 데이터를 주고받을 수 있도록 구성합니다.

FastAPI 자체는 HTTP 기반의 Web API 서버이므로 MQTT 메시지를 직접 처리하지는 않습니다. 따라서 main.py에서 MQTT Client 기능을 함께 실행하여 Mosquitto Broker에 연결합니다.

전체 구조는 다음과 같습니다.

ESP32 ↔ Mosquitto MQTT Broker ↔ FastAPI

여기서 Mosquitto는 MQTT 메시지를 중계하고, FastAPI는 MQTT Client로 Broker에 접속하면서 동시에 브라우저나 다른 프로그램에서 사용할 수 있는 Web API를 제공합니다.

이번 구성에서는 ESP32가 esp32/test Topic으로 전송하는 값을 FastAPI가 Subscribe하여 저장합니다. 이후 브라우저에서 FastAPI의 API를 호출하면 마지막으로 수신된 값을 확인할 수 있습니다.

반대 방향으로는 FastAPI가 esp32/control Topic으로 메시지를 Publish하고, 이 Topic을 Subscribe하고 있는 ESP32가 해당 메시지를 수신합니다.

따라서 두 방향의 통신은 다음과 같이 구성됩니다.

ESP32 → FastAPI

ESP32
→ Publish : esp32/test
→ Mosquitto Broker
→ FastAPI Subscribe
→ Web Browser에서 값 확인

FastAPI → ESP32

Web Browser
→ FastAPI API 호출
→ Publish : esp32/control
→ Mosquitto Broker
→ ESP32 Subscribe

이 구조에서 ESP32와 FastAPI는 서로 직접 연결되지 않습니다. 두 장치는 각각 Mosquitto Broker에 연결되며, Topic을 기준으로 메시지를 주고받습니다.

이러한 구조의 장점은 ESP32의 IP 주소나 연결 정보를 FastAPI가 직접 관리할 필요가 없다는 것입니다. 또한 이후 ESP32가 여러 대로 늘어나거나 다른 프로그램이 추가되더라도 동일한 MQTT Broker를 중심으로 시스템을 확장할 수 있습니다.

다음으로 Orange Pi One의 main.py에 MQTT 기능을 추가하여 실제로 FastAPI와 Mosquitto를 연결해 보겠습니다.

서버의 main.py를 수정하기 위해 Visual Studio Code의 Remote SSH를 이용하여 Orange Pi One에 접속합니다.

아래는 main.py의 전체 코드 입니다.

MQTT Client 설정 및 메시지 수신

먼저 FastAPI에서 MQTT를 사용하기 위해 Paho MQTT Client 라이브러리를 Import합니다.

import paho.mqtt.client as mqtt

MQTT Broker와 Subscribe할 Topic은 다음과 같이 설정합니다.

MQTT_BROKER = "localhost"
MQTT_PORT = 1883
MQTT_TOPIC = "esp32/test"

Mosquitto와 FastAPI가 동일한 Orange Pi One에서 실행되고 있으므로 Broker 주소는 localhost를 사용합니다. Port는 Mosquitto의 기본 MQTT Port인 1883입니다.

FastAPI는 ESP32가 데이터를 Publish하는 esp32/test Topic을 Subscribe합니다.

MQTT Broker와 연결되면 on_connect() Callback 함수가 호출됩니다.

def on_connect(client, userdata, flags, rc):
    print("MQTT connected:", rc)

    client.subscribe(MQTT_TOPIC)
    print("Subscribed:", MQTT_TOPIC)

연결이 완료되면 client.subscribe()를 이용하여 esp32/test Topic을 Subscribe합니다.

ESP32가 이 Topic으로 메시지를 Publish하면 Mosquitto가 FastAPI의 MQTT Client로 메시지를 전달하고 on_message() 함수가 호출됩니다.

def on_message(client, userdata, msg):
    global latest_value

    latest_value = msg.payload.decode()

    print("MQTT received:",
          msg.topic,
          latest_value)

수신된 Payload는 msg.payload.decode()를 이용하여 문자열로 변환하고 latest_value에 저장합니다.

따라서 ESP32가 다음과 같이 값을 Publish하면,

ESP32 → esp32/test → Mosquitto → FastAPI

FastAPI는 가장 최근에 수신한 값을 latest_value에 보관하게 됩니다.

마지막으로 MQTT Client를 생성하고 Callback 함수를 등록한 후 Mosquitto에 연결합니다.

mqtt_client = mqtt.Client()

mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
mqtt_client.loop_start()

loop_start()는 MQTT의 네트워크 처리를 별도의 Thread에서 실행합니다. 따라서 FastAPI Web Server가 동작하는 동안에도 MQTT 메시지를 계속 수신할 수 있습니다.

결과적으로 하나의 main.py 안에서 FastAPI Web Server와 MQTT Client가 동시에 동작하게 됩니다.

FastAPI에서 MQTT 데이터 확인 및 제어

FastAPI에서는 MQTT로 수신한 데이터를 브라우저에서 확인할 수 있도록 API를 제공합니다.

먼저 기본 주소 /에서는 서버가 정상적으로 동작하고 있는지 간단히 확인합니다.

@app.get("/")
def root():
    return {
        "message": "FastAPI MQTT Server"
    }

브라우저에서 FastAPI의 기본 주소에 접속하면 다음과 같은 메시지를 확인할 수 있습니다.

{"message":"FastAPI MQTT Server"}

다음 /esp32 API에서는 MQTT를 통해 ESP32로부터 가장 최근에 수신한 값을 반환합니다.

@app.get("/esp32")
def get_esp32_value():
    return {
        "topic": MQTT_TOPIC,
        "value": latest_value
    }

ESP32는 esp32/test Topic으로 숫자를 Publish하고 있으며, FastAPI는 이 Topic을 Subscribe하여 latest_value에 저장합니다.

따라서 데이터 흐름은 다음과 같습니다.

ESP32 → Publish esp32/test → Mosquitto → FastAPI → /esp32 → Web Browser

브라우저에서 /esp32에 접속하면 현재 FastAPI가 마지막으로 수신한 MQTT 값을 확인할 수 있습니다.

반대 방향의 제어는 /control/{command} API에서 처리합니다.

@app.get("/control/{command}")
def control(command: str):
    mqtt_client.publish("esp32/control", command)

    return {
        "topic": "esp32/control",
        "command": command
    }

브라우저에서 전달한 command 값은 FastAPI의 mqtt_client.publish()를 통해 esp32/control Topic으로 Publish됩니다.

ESP32는 앞에서 esp32/control Topic을 Subscribe하도록 설정하였으므로, Mosquitto Broker를 통해 이 명령을 수신합니다.

예를 들어 브라우저에서 ON 명령을 전달하면 전체 흐름은 다음과 같습니다.

Web Browser → FastAPI → Publish esp32/control → Mosquitto → ESP32

ESP32에서는 MQTT_EVT_PUBLISH Event가 발생하고 수신된 Payload인 ON을 확인할 수 있습니다.

따라서 /esp32/control/{command}는 서로 반대 방향의 데이터 흐름을 담당합니다.

  • /esp32 : MQTT → FastAPI → Browser
  • /control/{command} : Browser → FastAPI → MQTT

이렇게 FastAPI와 MQTT를 연결하면 브라우저는 MQTT 프로토콜을 직접 사용하지 않고도 HTTP API를 통해 ESP32의 데이터를 확인하거나 제어 명령을 전달할 수 있습니다.

결국 FastAPI는 Web과 MQTT 사이를 연결하는 중간 인터페이스 역할을 하게 됩니다.

빌드 및 전체 동작 확인

ESP32 프로젝트 Build, 다운로드, 실행확인

모든것이 준비가 되었으므로 이제 빌드를 해서 동작을 확인해 볼 차례 입니다.

이전 프로젝트와 마찬가지로 CHANGELOG.md에 있는 Build 명령어를 복사하여 빌드를 하겠습니다.

물론 이 명령은 반듯이 해당 프로젝트로 폴더를 옮긴 후에 실행해야 합니다.

빌드 명령은 다음과 같습니다.

다음 화면은 빌드가 성공한 화면입니다.

다음으로 west flash 하여 펌웨어를 타겟보드에 다운로드 하겠습니다.

다음과 같은 메시지가 나타나면서 다운로드가 성공 하였습니다.

Console을 Open하여 실행을 확인해 보겠습니다.

Console의 출력 내용을 보면 ESP32가 Wi-Fi에 정상적으로 연결되고 192.168.0.9의 IP 주소를 할당받은 것을 확인할 수 있습니다.

이후 Orange Pi One의 Mosquitto MQTT Broker인 192.168.0.7:1883에 접속하였으며, 다음 메시지를 통해 MQTT 연결과 Subscribe가 정상적으로 이루어진 것을 확인할 수 있습니다.

MQTT connected
Subscribed to esp32/control

이후 esp32/test Topic으로 0, 1, 2와 같이 숫자가 1초 간격으로 계속 Publish되고 있습니다.

Published: esp32/test -> 0
Published: esp32/test -> 1
Published: esp32/test -> 2
...

따라서 현재까지 ESP32 → Mosquitto MQTT Broker 방향의 MQTT 연결과 Publish가 정상적으로 동작하고 있음을 확인할 수 있습니다.

다음으로 MQTT Explorer를 이용하여 실제로 Broker에 esp32/test 메시지가 전달되고 있는지 확인해 보겠습니다.

MQTT Explorer로 메세지 수신 확인

다음은 Windows의 MQTT Tool인 MQTT Explorer로 ESP32에서 발행(Publish)하는 메시지를 수신하는지 확인해 보겠습니다.

Host를 192.168.0.7, Port를 1883을 입력하고 CONNECT를 누릅니다.

CONNECT 버튼을 누르면 다음과 같은 화면이 나타납니다.

esp32 토픽에서 test = 329가 표시된 것을 확인할 수 있습니다.

ESP32에서 esp32/test Topic으로 1초마다 증가하는 숫자를 Publish하고 있으며, MQTT Explorer에서 해당 메시지가 정상적으로 수신되고 있음을 확인할 수 있습니다.

MQTT Explorer와 ESP32 Console을 같이 캡쳐한 화면입니다.

ESP32 Console에서 마지막으로 Publish한 값 37과 MQTT Explorer에서 확인되는 esp32/test의 값 37이 동일합니다. 따라서 ESP32에서 Publish한 MQTT 메시지가 Mosquitto Broker를 통해 정상적으로 전달되고 있음을 확인할 수 있습니다.

MQTT Explorer로 메시지 송신

MQTT Explorer로 메시지를 송신해서 ESP32에서 메시지를 수신하는 것을 해 보겠습니다.

Topic을 esp32/control로 하고 메시지 입력란에 On 이나 Off을 입력하고 Publish 버튼을 누르면 발행되고 ESP32 Console에서는 수신이 됩니다.

ESP32는 프로그램에서 esp32/control Topic을 이미 Subscribe한 상태입니다.

아래는 실행 화면 입니다.

MQTT Explorer에서 esp32/control Topic으로 On 메시지를 Publish하면 Mosquitto MQTT Broker가 해당 Topic을 Subscribe하고 있는 ESP32로 메시지를 전달합니다.

ESP32의 Console에서는 다음과 같이 수신된 Topic과 Payload를 확인할 수 있습니다.

MQTT message received
Topic: esp32/control
Payload: On

따라서 메시지의 전달 과정은 다음과 같습니다.

MQTT Explorer → Publish (esp32/control) → Mosquitto MQTT Broker → ESP32

앞에서는 ESP32가 esp32/test Topic으로 메시지를 Publish하는 것을 확인하였고, 이번에는 반대로 ESP32가 esp32/control Topic의 메시지를 수신하는 것을 확인하였습니다.

이를 통해 ESP32 ↔ Mosquitto MQTT Broker 간의 양방향 MQTT 통신이 정상적으로 동작하고 있음을 확인할 수 있습니다.

FastAPI와 MQTT 양방향 통신 확인

이번 단계가 본 프로젝트의 최종 목표입니다.

ESP32에서 MQTT 메시지를 Publish하면 FastAPI를 통해 웹 브라우저에서 해당 값을 확인할 수 있고, 반대로 웹 브라우저에서 명령을 보내면 FastAPI가 MQTT 메시지로 Publish하여 ESP32에서 수신할 수 있습니다.

Orange Pi One에서는 Mosquitto MQTT Broker와 FastAPI Web Server가 함께 동작하며, FastAPI는 MQTT Client 역할도 수행합니다.

따라서 전체적인 양방향 데이터 흐름은 다음과 같습니다.

ESP32 → Mosquitto → FastAPI → Web Browser

Web Browser → FastAPI → Mosquitto → ESP32

이제 실제로 두 방향의 통신이 정상적으로 이루어지는지 차례로 확인해 보겠습니다.

가장 먼저 할 일은 웹서버를 가동 시키는 일 입니다.

Visual Studio Code의 Remote SSH Terminal uvicorn 웹서버를 가동 시킵니다.

이때 앞에서 구성한 Python 가상 환경(venv)을 활성화한 상태에서 실행합니다.

아래는 uvicorn이 샐행된 화면입니다;

웹서버가 실행이 되었으므로 브라우저로 ESP32에서 Publish한 토픽이 수신되는 지 확인 하겠습니다.

브라우저의 주소 입력창에 http://192.168.0.7:8000/esp32 를 입력합니다.

실행을 하면 아래와 같이 “topic”과 “value” 가 나타납니다.

웹 브라우저에서는 FastAPI가 MQTT를 통해 마지막으로 수신한 topicvalue가 표시됩니다.

여기서 새로고침을 반복하면 value가 계속 증가하는 것을 확인할 수 있습니다. 이는 ESP32가 esp32/test Topic으로 1초마다 Publish한 값이 Mosquitto MQTT Broker를 거쳐 FastAPI까지 정상적으로 전달되고 있음을 의미합니다.

따라서 다음과 같은 데이터 전달 경로가 정상적으로 동작하는 것을 확인하였습니다.

ESP32 → Mosquitto MQTT Broker → FastAPI → Web Browser

다음으로 웹브라우저에서 FastAPI의 제어 API를 호출하고, FastAPI가 MQTT 메시지를 Publish하여 ESP32에서 수신되는지 확인해 보겠습니다.

웹브라우저 주소창에 http://192.168.0.7:8000/control/On 을 입력합니다.

아래와 같습니다.

FastAPI는 이 요청을 받으면 Mosquitto Broker의 esp32/control Topic으로 on 메시지를 Publish합니다.

ESP32 Console 화면에서 다음과 같이 Topic: esp32/control, Payload: on이 출력되는 것을 확인할 수 있습니다.

이번에는 /control/off를 입력합니다.

마찬가지로 FastAPI가 esp32/control Topic으로 off를 Publish하고, ESP32에서는 Payload: off가 수신되는 것을 확인할 수 있습니다.

아래는 Console 화면입니다.

이를 통해 다음과 같은 제어 방향의 통신이 정상적으로 동작함을 확인하였습니다.

Web Browser → FastAPI → Mosquitto → ESP32

ESP32는 esp32/test Topic으로 숫자를 계속 Publish하는 동시에, esp32/control Topic을 Subscribe하여 onoff 메시지를 수신하고 있습니다. 따라서 하나의 MQTT 연결에서 데이터 전송과 제어 명령 수신이 동시에 이루어지는 양방향 통신을 확인할 수 있습니다.

결론

이번 실습에서는 Zephyr 기반 ESP32를 Wi-Fi에 연결하고, Orange Pi에서 동작하는 Mosquitto Broker를 이용하여 MQTT 통신을 구현하였습니다.

ESP32에서 esp32/test Topic으로 데이터를 주기적으로 Publish하고, mosquitto_sub와 MQTT Explorer를 통해 메시지가 정상적으로 전달되는 것을 확인하였습니다. 또한 ESP32가 esp32/control Topic을 Subscribe하도록 구성하여 on, off 메시지를 수신하는 것도 확인하였습니다.

마지막으로 MQTT와 FastAPI를 연결하여 다음과 같은 양방향 통신 구조를 완성하였습니다.

ESP32 → Mosquitto → FastAPI → Web Browser
ESP32 ← Mosquitto ← FastAPI ← Web Browser

이를 통해 ESP32에서 발생한 데이터를 웹에서 확인하고, 반대로 웹에서 전달한 명령을 ESP32에서 수신할 수 있었습니다.

앞서 UDP와 TCP 실습에서는 ESP32와 상대 장치가 직접 통신하는 구조를 살펴보았다면, 이번 MQTT 실습에서는 Broker와 Topic을 이용하여 여러 장치와 응용 프로그램을 서로 분리하면서 데이터를 주고받는 구조를 확인할 수 있었습니다.

현재는 테스트를 위해 숫자와 on/off 메시지를 사용했지만, 이를 실제 센서 데이터와 GPIO, Relay 등의 제어로 확장하면 원격 모니터링 및 제어가 가능한 IoT 시스템으로 발전시킬 수 있습니다.

이번 실습을 통해 Zephyr ESP32 → MQTT → FastAPI → Web으로 이어지는 기본적인 IoT 통신 구조를 직접 구현하고 확인하였습니다.

댓글 남기기