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

서론

지금까지 ESP32와 Zephyr RTOS를 이용하여 Wi-Fi Scan, AP 연결, UDP 및 TCP 통신을 차례로 실습하였습니다. 이를 통해 ESP32가 Wi-Fi 네트워크에 접속하고 PC와 데이터를 주고받는 기본적인 네트워크 통신 과정을 확인하였습니다.

이번 글부터는 범위를 조금 확장하여 Orange Pi와 Linux를 이용한 IoT 서버 환경을 구축해 보겠습니다.

Orange Pi는 Linux 운영체제를 실행할 수 있는 SBC(Single Board Computer)로, 네트워크에 연결하여 다양한 서버 프로그램을 실행할 수 있습니다. 이번 실습에서는 Orange Pi에 Linux 이미지를 설치하고 SSH를 이용한 원격 접속 환경을 구성한 뒤, Mosquitto MQTT Broker와 FastAPI 서버를 구축하겠습니다.

Mosquitto는 MQTT 메시지를 중계하는 Broker의 역할을 하며, FastAPI는 PC나 웹 애플리케이션에서 HTTP API를 통해 IoT 장치를 제어할 수 있는 인터페이스를 제공합니다. 마지막에는 FastAPI와 Mosquitto를 연결하여 API 요청을 MQTT 메시지로 전달하는 과정까지 확인하겠습니다.

이번 글에서는 Linux 자체를 깊이 있게 다루기보다는 Orange Pi를 IoT 서버로 구성하는 데 필요한 Linux 부팅, 네트워크 연결, SSH 원격 접속 및 기본적인 서버 실행 환경​을 중심으로 살펴보겠습니다.

이를 통해 이후 ESP32의 MQTT 통신을 추가하여 다음과 같은 IoT 시스템으로 확장할 수 있는 기반을 마련하고자 합니다.

PC / Web → FastAPI → Mosquitto MQTT Broker → ESP32 → IoT Device

Orange Pi와 IoT 서버

Orange Pi를 IoT 서버로 사용하는 이유

ESP32와 같은 MCU에서도 네트워크 통신과 간단한 서버 기능을 구현할 수 있지만, 여러 장치의 데이터를 수집하거나 MQTT Broker, Web API, 데이터베이스와 같은 다양한 서비스를 함께 운영하려면 보다 확장된 시스템이 필요합니다.

Orange Pi는 ARM 프로세서를 기반으로 Linux를 실행할 수 있는 SBC(Single Board Computer)​입니다. 일반적인 MCU와 달리 운영체제를 기반으로 동작하기 때문에 Mosquitto, Python, FastAPI와 같은 다양한 소프트웨어를 비교적 쉽게 설치하고 실행할 수 있습니다.

또한 크기가 작고 소비 전력이 낮아 항상 켜 두어야 하는 소규모 IoT 서버로 활용하기에도 적합합니다. 네트워크에 연결한 후에는 SSH를 이용하여 PC에서 원격으로 접속할 수 있으므로 모니터와 키보드를 계속 연결해 둘 필요도 없습니다.

이번 실습에서는 Orange Pi를 다음과 같은 역할을 수행하는 IoT 서버로 사용합니다.

  • Linux : 서버 프로그램이 동작하는 운영체제 환경
  • Mosquitto : ESP32와 서버 사이의 MQTT 메시지를 중계하는 Broker
  • FastAPI : PC나 웹에서 HTTP를 이용하여 서버 기능을 호출하기 위한 API
  • SSH : PC에서 Orange Pi를 원격으로 관리하기 위한 접속 방법

이와 같이 Orange Pi를 사용하면 MCU에서 센서와 장치를 제어하고, Linux 서버에서 통신과 데이터 처리를 담당하는 형태로 시스템의 역할을 나누어 구성할 수 있습니다.

Orange Pi는 제가 오래전에 사 두었었는데 이번에 IoT 프로젝트를 하면서 필요하게 되어 한번 살려 보기로 하였습니다.

Orange Pi는 다음과 같이 구성되어 있습니다.

좀더 자세한 사항은 Orange Pi 공식 홈페이지에서 확인할 수 있습니다.

Orange Pi One

전체 시스템 구성

이번 실습에서는 Orange Pi에 Linux를 설치하고, 그 위에서 Mosquitto MQTT Broker와 FastAPI 서버를 함께 실행합니다.

전체 시스템의 기본 구성은 다음과 같습니다.

PC나 웹에서는 HTTP를 이용하여 FastAPI에 요청을 보낼 수 있습니다. FastAPI는 이 요청을 처리하고 필요한 경우 Mosquitto MQTT Broker에 MQTT 메시지를 Publish합니다.

Mosquitto는 전달받은 MQTT 메시지를 해당 Topic을 Subscribe하고 있는 ESP32와 같은 IoT 장치에 전달합니다. 반대로 ESP32에서 센서 데이터나 장치 상태를 MQTT로 Publish하여 Orange Pi로 전달하는 구성도 가능합니다.

이번 글에서는 먼저 Orange Pi의 Linux 환경을 구축한 후 Mosquitto와 FastAPI를 각각 설치하고 동작을 확인합니다. 마지막에는 FastAPI에서 MQTT 메시지를 Publish하여 Mosquitto와 연결되는 과정까지 확인합니다.

ESP32와의 실제 MQTT 통신은 이후 Zephyr ESP32 MQTT 실습에서 진행할 예정입니다.

따라서 이번 글은 Orange Pi를 Linux 기반 IoT 서버로 구성하고, 이후 ESP32를 연결하기 위한 서버 측 기반을 준비하는 과정​이라고 할 수 있습니다.

Orange Pi Linux 환경 구축

Armbian Imager 준비

Orange Pi를 IoT 서버로 사용하기 위해서는 먼저 Orange Pi에서 실행할 Linux 운영체제를 준비해야 합니다.

ESP32와 같은 MCU에서는 작성한 펌웨어를 Flash 메모리에 직접 다운로드하여 실행하지만, Orange Pi와 같은 SBC(Single Board Computer)는 Linux와 같은 운영체제를 저장장치에 설치하고 이를 부팅하여 사용합니다.

이번 실습에서는 Orange Pi의 Linux 환경을 구성하기 위해 Armbian을 사용하였습니다. Armbian은 ARM 기반의 SBC를 위한 Linux 배포판으로 Orange Pi를 비롯한 다양한 보드를 지원합니다.

Linux를 설치하기 위해서는 먼저 PC에 Armbian Imager를 준비합니다. Armbian Imager는 사용할 보드와 운영체제를 선택하면 필요한 Linux 이미지를 다운로드하고, 이를 microSD 카드와 같은 저장장치에 기록할 수 있도록 도와주는 프로그램입니다.

따라서 사용자가 Linux 이미지 파일을 별도로 다운로드하여 기록하는 과정을 거치지 않고, Armbian Imager 안에서 다음 과정을 순서대로 진행할 수 있습니다.

보드 제조사 선택 → 보드 선택 → 운영체제 선택 → 저장장치 선택 → Linux 이미지 다운로드 → microSD 카드 기록

먼저 PC에 Armbian Imager를 다운로드하여 설치한 후 실행합니다.

Armbian Imager를 실행하면 아래와 같이 첫 화면이 나타납니다.

화면 상단에는 다음과 같이 Linux 설치를 위한 과정이 순서대로 표시됩니다.

MANUFACTURER → BOARD → OS → STORAGE

먼저 MANUFACTURER에서 Orange Pi를 선택하고, 이어서 사용하고 있는 보드와 설치할 운영체제, 그리고 Linux 이미지를 기록할 저장장치를 차례로 선택합니다.

다음 절에서는 Armbian Imager를 이용하여 Orange Pi에 사용할 Linux 이미지를 선택하고 microSD 카드에 기록하는 과정을 살펴보겠습니다.

Linux 이미지 다운로드 및 microSD 카드 기록

Armbian Imager를 실행한 후 Orange Pi에 사용할 Linux 이미지와 이미지를 기록할 microSD 카드를 차례로 선택합니다.

먼저 MANUFACTURER에서 Orange Pi를 선택합니다.

다음으로 BOARD에서 이번 실습에 사용하는 Orange Pi One을 선택합니다.

이번 실습은 Orange Pi를 IoT 서버로 사용하기 위한 것이므로 데스크톱 환경보다는 서버 운영에 필요한 최소 환경을 갖춘 Minimal 이미지를 선택하는 것이 적합합니다.

화면에는 XFCEMinimal 이미지가 표시됩니다. XFCE는 그래픽 데스크톱 환경을 포함하고 있어 모니터와 마우스를 이용하여 일반 PC와 비슷하게 사용할 수 있습니다. 반면 Minimal은 그래픽 데스크톱 환경을 제외하고 필요한 기본 기능 위주로 구성되어 상대적으로 가볍습니다.

이번 실습에서는 이후 Orange Pi에 SSH로 원격 접속하여 Mosquitto와 FastAPI를 실행할 것이므로 그래픽 데스크톱 환경이 필요하지 않습니다. 따라서 Armbian Minimal 이미지를 선택하겠습니다.

Armbian Minimal 이미지를 선택한 다음 STORAGE에서 Linux 이미지를 기록할 저장장치를 선택합니다.

PC에 microSD 카드를 연결하면 Armbian Imager의 저장장치 목록에 해당 카드가 표시됩니다. 용량과 장치 정보를 확인한 후 Linux를 설치할 microSD 카드를 선택합니다.

이번 실습에서는 Orange Pi가 microSD 카드에서 부팅하도록 구성하므로, Linux 이미지를 기록할 microSD 카드가 필요합니다.

이때 저장장치를 잘못 선택하면 해당 장치에 저장되어 있던 데이터가 삭제될 수 있으므로 선택한 저장장치가 microSD 카드가 맞는지 반드시 확인해야 합니다.

microSD카드를 선택하는 화면은 올리지 않겠습니다.

보드와 운영체제, 저장장치 선택이 모두 완료되면 이미지 기록을 시작합니다. Armbian Imager는 선택한 Orange Pi용 Armbian Linux 이미지를 인터넷에서 다운로드한 후 microSD 카드에 기록합니다.

이미지 기록이 진행되는 동안에는 microSD 카드를 PC에서 분리하지 않습니다. 다운로드 및 기록에 필요한 시간은 인터넷 연결 속도와 microSD 카드의 쓰기 속도에 따라 달라질 수 있습니다.

기록이 정상적으로 완료되면 microSD 카드에는 Orange Pi가 부팅할 수 있는 Armbian Linux 시스템이 준비됩니다.

microSD 카드를 PC에서 안전하게 분리한 후 Orange Pi One의 microSD 카드 슬롯에 장착합니다.

이제 Orange Pi에 전원을 연결하여 Armbian Linux를 처음 부팅해 보겠습니다.

Orange Pi 부팅

Armbian Linux 이미지가 기록된 microSD 카드를 Orange Pi One에 장착한 후 전원을 연결합니다.

Orange Pi는 일반적인 MCU 보드와 달리 전원을 인가한 직후 사용자 프로그램이 바로 실행되는 것이 아니라, microSD 카드에 설치된 Linux 운영체제를 부팅하는 과정을 거칩니다. 따라서 처음 부팅할 때는 시스템 초기화 작업으로 인해 다소 시간이 걸릴 수 있습니다.

처음 부팅하면 Armbian의 초기 로그인 및 사용자 계정 설정 과정을 진행합니다.

Armbian 이미지의 종류나 버전에 따라 초기 로그인 방법에는 차이가 있을 수 있습니다. 화면에 표시되는 안내에 따라 초기 Login ID와 Password를 입력하여 로그인합니다.

처음 로그인한 후에는 보안을 위해 비밀번호를 변경하거나 새로운 일반 사용자 계정을 생성하는 과정이 진행될 수 있습니다.

Linux에서는 시스템 관리 권한을 가진 root 계정과 일반적인 작업에 사용하는 사용자 계정(User Account)​을 구분하여 사용합니다.

root는 Linux 시스템 전체를 관리할 수 있는 최고 관리자 계정입니다. 모든 파일과 시스템 설정을 변경할 수 있기 때문에 일상적인 작업을 항상 root 계정으로 수행하는 것은 권장되지 않습니다.

따라서 초기 설정 과정에서 앞으로 사용할 일반 사용자 계정의 Login ID와 Password를 설정합니다.

아래는 생성한 사용자 계정으로 로그인한 화면입니다.

저는 사용자 계정 이름을 orangepi로 지정하였습니다.

이후에는 생성한 사용자 계정으로 로그인하여 Orange Pi를 사용합니다. 시스템 설정이나 프로그램 설치와 같이 관리자 권한이 필요한 작업은 일반 사용자 계정에서 sudo 명령을 이용하여 실행할 수 있습니다.

예를 들어 이후 Mosquitto를 설치할 때 다음과 같이 사용합니다.

sudo apt install mosquitto

여기서 sudo는 일반 사용자에게 필요한 명령에 한하여 관리자 권한으로 실행할 수 있도록 해 줍니다.

이때 생성한 Login ID와 Password는 이후 PC에서 Orange Pi에 SSH로 원격 접속할 때도 사용하므로 기억해 두어야 합니다.

초기 로그인과 사용자 계정 설정까지 완료되면 Orange Pi의 기본적인 Linux 부팅 환경이 준비된 것입니다.

네트워크 연결 및 IP 주소 확인

Orange Pi에 Linux가 정상적으로 부팅되었으면 다음으로 네트워크 연결 상태와 IP 주소를 확인합니다.

이번 실습에서는 Orange Pi One의 Ethernet 포트와 공유기를 LAN 케이블로 연결하였습니다. 네트워크가 정상적으로 연결되면 공유기의 DHCP(Dynamic Host Configuration Protocol) 기능을 통해 Orange Pi에 IP 주소가 자동으로 할당됩니다.

Linux 터미널에서 네트워크 인터페이스와 IP 주소를 확인하려면 다음 명령을 사용할 수 있습니다.

ip addr

명령을 실행하면 현재 Orange Pi에서 사용하고 있는 네트워크 인터페이스와 각각의 IP 주소가 표시됩니다.

Ethernet 인터페이스 항목에서 다음과 같이 inet 뒤에 표시되는 주소를 확인합니다.

inet 192.168.x.x/24

여기서 192.168.x.x 부분이 현재 Orange Pi에 할당된 IPv4 주소입니다.

이 IP 주소는 이후 PC에서 Orange Pi에 SSH로 원격 접속하거나 FastAPI 서버에 접속하고, ESP32에서 Mosquitto MQTT Broker에 연결할 때 사용​하게 됩니다.

Orange Pi와 PC가 같은 공유기에 연결되어 있다면 일반적으로 동일한 로컬 네트워크에 속하게 되며, PC에서 Orange Pi의 IP 주소를 이용하여 통신할 수 있습니다.

DHCP를 사용하면 공유기가 자동으로 IP 주소를 할당하므로 설정이 간단하지만, Orange Pi를 계속 IoT 서버로 사용할 경우 재부팅이나 네트워크 환경에 따라 IP 주소가 변경될 가능성이 있습니다.

따라서 초기 동작 확인은 DHCP로 진행하고, 이후 필요에 따라 Orange Pi가 항상 같은 IP 주소를 사용하도록 고정 IP(Static IP)​를 설정할 수 있습니다.

네트워크 연결과 Orange Pi의 IP 주소까지 확인했으면 이제 모니터와 키보드를 직접 사용하지 않고, PC에서 SSH를 이용하여 Orange Pi의 Linux 터미널에 원격으로 접속할 수 있습니다.

SSH 원격 접속

Windows PowerShell에서 SSH 접속 확인

Orange Pi의 네트워크 연결과 IP 주소를 확인했으면 이제 PC에서 SSH(Secure Shell)​를 이용하여 Orange Pi에 원격으로 접속해 보겠습니다.

SSH는 네트워크를 통해 다른 컴퓨터의 터미널에 안전하게 접속할 수 있도록 해 주는 프로토콜입니다. SSH를 사용하면 Orange Pi에 모니터와 키보드를 직접 연결하지 않아도 PC에서 Linux 명령을 실행하고 서버를 관리할 수 있습니다.

이번 실습에서는 Windows PC의 PowerShell을 이용하여 Orange Pi에 접속합니다.

PowerShell을 실행하고 다음과 같이 ssh 명령을 입력합니다.

ssh orangepi@192.168.x.x

여기서 orangepi는 앞에서 생성한 Linux 사용자 계정(Login ID)​이며, 192.168.x.xip addr 명령으로 확인한 Orange Pi의 IP 주소입니다.

처음 SSH로 접속하는 경우 해당 서버를 신뢰할 것인지 확인하는 메시지가 나타날 수 있습니다. 이때 접속하려는 Orange Pi의 주소가 맞는지 확인한 후 yes를 입력합니다.

이어서 사용자 계정의 Password를 입력합니다. Linux 터미널에서는 보안을 위해 Password를 입력해도 화면에 문자나 *가 표시되지 않을 수 있습니다. 입력 내용은 정상적으로 전달되고 있으므로 Password를 입력한 후 Enter 키를 누릅니다.

접속에 성공하면 PC의 PowerShell 화면에 다음과 같이 Orange Pi의 Linux 프롬프트가 나타납니다.

orangepi@orangepione:~$

이 프롬프트가 나타나면 현재 명령은 Windows PC가 아니라 네트워크로 연결된 Orange Pi의 Linux에서 실행됩니다.

이제 Orange Pi에 연결했던 모니터와 키보드가 없어도 PC에서 Linux 명령을 실행하고 파일을 편집하거나 Mosquitto와 FastAPI 같은 서버 프로그램을 설치하고 실행할 수 있습니다.

SSH 연결을 종료하려면 다음 명령을 입력합니다.

exit

VS Code Remote – SSH 접속

PowerShell에서 SSH 접속이 정상적으로 되는 것을 확인했으면, 이후 작업은 Visual Studio Code의 Remote – SSH 기능을 이용하면 더욱 편리합니다.

Remote – SSH를 사용하면 Windows PC에서 실행한 Visual Studio Code가 네트워크를 통해 Orange Pi에 접속하여, Orange Pi의 파일을 직접 열고 수정하거나 Linux 터미널을 사용할 수 있습니다.

먼저 Visual Studio Code에서 Remote – SSH 확장 기능을 설치합니다.

설치 관련된 내용은 따로 설명하지 않겠습니다. 인터넷의 여러가지 자료들이 있으므로 참조하면 됩니다.

아래는 Visual Studio Code에서 Remote – SSH를 이용하여 Orange Pi에 접속한 화면입니다.

접속이 완료되면 Visual Studio Code의 왼쪽 Explorer에서 Orange Pi의 디렉터리와 파일을 직접 열어볼 수 있으며, Terminal을 실행하면 Orange Pi의 Linux 터미널을 사용할 수 있습니다.

따라서 이후 Mosquitto 설정이나 FastAPI의 Python 소스 파일을 작성할 때 별도의 모니터와 키보드를 사용하지 않고 Windows PC의 Visual Studio Code에서 Orange Pi를 직접 개발 환경처럼 사용할 수 있습니다.

이번 실습에서는 이후 대부분의 작업을 Visual Studio Code의 Remote – SSH 환경에서 진행하겠습니다.

Linux 기본 환경 확인

Visual Studio Code의 Remote – SSH를 이용하여 Orange Pi에 접속했으므로, Mosquitto와 FastAPI를 설치하기 전에 Linux에서 사용할 몇 가지 기본 명령을 간단히 확인해 보겠습니다.

이번 글의 목적은 Linux 자체를 자세히 학습하는 것이 아니므로, 이후 IoT 서버를 구축하는 과정에서 필요한 기본적인 명령만 살펴보겠습니다.

VS Code에서 Terminal을 열면 Orange Pi의 Linux 터미널을 바로 사용할 수 있습니다.

현재 작업하고 있는 디렉터리를 확인하려면 pwd 명령을 사용합니다.

현재 디렉터리에 있는 파일과 디렉터리를 확인하려면 ls 명령을 사용합니다.

다른 디렉터리로 이동할 때는 cd 명령을 사용합니다.

Linux에서는 필요한 프로그램을 패키지(Package) 형태로 설치하여 사용할 수 있습니다. 이번에 설치한 Armbian은 Debian 계열의 Linux이므로 패키지 관리에 APT(Advanced Package Tool)​를 사용할 수 있습니다.

프로그램을 설치하기 전에는 “sudo apt update” 명령으로 설치 가능한 패키지 정보를 갱신할 수 있습니다.

apt update를 실행한 후 다음과 같은 메시지가 나타날 수 있습니다.

1 package can be upgraded. Run 'apt list --upgradable' to see it.

이는 현재 설치되어 있는 패키지 중 새로운 버전으로 업데이트할 수 있는 패키지가 1개 있다는 의미입니다.

업그레이드할 수 있는 패키지를 확인하려면 다음 명령을 사용할 수 있습니다.

apt list --upgradable

실제로 설치되어 있는 패키지를 최신 버전으로 업그레이드하려면 다음 명령을 실행합니다.

sudo apt upgrade

이 과정에서 업그레이드할 패키지 목록과 필요한 저장 공간 등이 표시되고 계속 진행할 것인지 확인하는 메시지가 나타날 수 있습니다. 내용을 확인한 후 진행하면 새로운 패키지가 다운로드되어 설치됩니다.

따라서 apt updateapt upgrade는 다음과 같이 역할이 다릅니다.

  • sudo apt update : 패키지 저장소에서 최신 패키지 목록을 가져옵니다.
  • sudo apt upgrade : 갱신된 패키지 목록을 기준으로 현재 설치된 패키지를 실제로 업그레이드합니다.

Linux 시스템을 처음 준비할 때는 다음과 같이 순서대로 실행할 수 있습니다.

sudo apt update
sudo apt upgrade

이후 필요한 프로그램은 apt install 명령으로 설치할 수 있습니다.

sudo apt install 패키지명

이번 실습에서도 이러한 과정을 통해 Linux의 패키지 정보를 갱신하고 필요한 패키지를 최신 상태로 준비한 후 Mosquitto MQTT Broker를 설치하겠습니다.

Mosquitto MQTT Broker 구축

Mosquitto 설치

Orange Pi의 Linux 환경이 준비되었으므로 이제 Mosquitto MQTT Broker를 설치하겠습니다.

MQTT는 IoT 환경에서 많이 사용하는 경량 메시지 통신 프로토콜입니다. MQTT에서는 장치들이 서로 직접 연결하여 메시지를 주고받기보다 중간에 있는 Broker를 통해 메시지를 전달합니다.

이번 실습에서는 Orange Pi에 Eclipse Mosquitto를 설치하여 MQTT Broker로 사용합니다.

앞에서 살펴본 APT를 이용하면 Mosquitto를 간단하게 설치할 수 있습니다. Mosquitto Broker와 MQTT 통신 테스트에 필요한 Client 도구는 다음 명령으로 설치할 수 있습니다.

sudo apt install mosquitto mosquitto-clients

여기서는 두 개의 패키지를 함께 설치합니다.

  • mosquitto : MQTT Broker 프로그램
  • mosquitto-clients : MQTT 메시지 송수신을 시험하기 위한 명령줄 프로그램

mosquitto-clients에는 이후 MQTT Publish / Subscribe 테스트에서 사용할 mosquitto_pubmosquitto_sub 명령이 포함되어 있습니다.

이번 Orange Pi에는 이미 Mosquitto와 mosquitto-clients를 설치하여 사용하고 있으므로 설치 과정을 다시 진행하지 않고, 현재 설치되어 있는 패키지를 확인하는 방법으로 설치 상태를 확인하겠습니다.

다음 명령을 실행합니다.

dpkg -l | grep mosquitto

이 명령은 현재 Linux에 설치된 패키지 목록에서 mosquitto가 포함된 항목을 찾아 표시합니다.

출력 결과에서 mosquittomosquitto-clients가 표시되면 두 패키지가 설치되어 있는 것을 확인할 수 있습니다.

Mosquitto를 설치하면 Broker를 Linux의 서비스(Service)​로 실행할 수 있습니다.

Linux의 서비스는 백그라운드에서 계속 실행되면서 특정 기능을 제공하는 프로그램이라고 생각할 수 있습니다. Mosquitto도 Terminal에서 매번 직접 실행하지 않고 서비스 형태로 실행하여 계속 MQTT Broker의 역할을 수행할 수 있습니다.

현재 Mosquitto 서비스의 동작 상태는 다음 명령으로 확인합니다.

sudo systemctl status mosquitto

위 화면은 현재 Orange Pi에서 실행한 결과입니다.

먼저 다음 부분에서 Mosquitto 서비스가 Linux에 정상적으로 등록되어 있고, 시스템 부팅 시 자동으로 시작하도록 설정되어 있음을 확인할 수 있습니다.

Loaded: loaded (...; enabled; preset: enabled)

여기서 loaded는 Mosquitto 서비스가 정상적으로 로드되었다는 의미이며, enabledOrange Pi가 부팅될 때 Mosquitto 서비스가 자동으로 시작되도록 설정되어 있다는 의미입니다.

현재 Mosquitto의 실제 실행 상태는 다음 부분에서 확인할 수 있습니다.

Active: active (running)

active (running)이 표시되어 있으므로 현재 Mosquitto MQTT Broker가 정상적으로 실행 중임을 알 수 있습니다.

또한 다음과 같이 실행 중인 Mosquitto 프로세스도 확인할 수 있습니다.

Main PID: 979 (mosquitto)

Linux에서는 실행 중인 각각의 프로세스에 PID(Process ID)를 부여합니다. 위 결과에서는 Mosquitto가 PID 979의 프로세스로 실행되고 있습니다. PID 값은 실행할 때마다 달라질 수 있으므로 숫자 자체는 중요하지 않습니다.

화면 아래쪽에는 Mosquitto 서비스가 시작되는 과정의 로그도 표시됩니다.

Loading config file /etc/mosquitto/conf.d/local.conf
Started mosquitto.service - Mosquitto MQTT Broker.

이를 통해 Mosquitto가 /etc/mosquitto/conf.d/local.conf 설정 파일을 읽은 후 MQTT Broker 서비스를 정상적으로 시작한 것도 확인할 수 있습니다.

따라서 현재 Orange Pi에서는 Mosquitto가 Linux 서비스로 등록되어 있으며, Orange Pi가 부팅되면 자동으로 시작되어 백그라운드에서 MQTT Broker로 동작하는 상태​입니다.

이제 Broker 자체가 정상적으로 실행되고 있음을 확인했으므로, 다음 단계에서는 mosquitto_pubmosquitto_sub을 이용하여 실제 MQTT 메시지가 Broker를 통해 전달되는지 확인해 보겠습니다.

Mosquitto 서비스 동작 확인

Mosquitto 설치가 완료되었으면 MQTT Broker의 설정을 확인하고 서비스를 실행해 보겠습니다.

Mosquitto는 기본 설정만으로도 로컬 환경에서 사용할 수 있지만, 이후 PC와 ESP32 등 Orange Pi 외부의 MQTT Client가 Broker에 접속하려면 외부 네트워크 연결을 위한 설정이 필요합니다.

이번 실습에서는 Mosquitto의 추가 설정 파일인 local.conf를 작성하여 MQTT의 기본 포트인 1883번 포트에서 외부 Client의 연결을 받을 수 있도록 설정하였습니다.

VS Code의 Terminal에서 다음 명령을 실행합니다.

sudo nano /etc/mosquitto/conf.d/local.conf

nano는 Linux Terminal에서 사용할 수 있는 간단한 텍스트 편집기입니다. 위 명령을 실행하면 관리자 권한으로 local.conf 파일을 생성하거나 편집할 수 있습니다.

local.conf에는 다음과 같이 입력합니다.

listener 1883
allow_anonymous true

listener 1883은 Mosquitto가 TCP 1883번 포트에서 MQTT Client의 연결을 기다리도록 설정합니다. 1883번 포트는 암호화되지 않은 일반 MQTT 통신에서 기본적으로 사용하는 포트입니다.

allow_anonymous true는 별도의 사용자 이름과 Password 인증 없이 MQTT Client가 Broker에 접속할 수 있도록 허용하는 설정입니다. 이번 실습에서는 로컬 네트워크에서 MQTT의 기본 동작을 확인하는 것이 목적이므로 간단한 테스트를 위해 이 설정을 사용하였습니다. 실제 외부 네트워크에서 Broker를 운영할 경우에는 사용자 인증과 보안 설정을 적용하는 것이 좋습니다.

아래는 실제 설정되어 있는 local.conf의 내용입니다.

nano에서 편집을 완료한 후 다음 순서로 파일을 저장하고 종료합니다.

Ctrl + O → Enter → Ctrl + X

설정 파일을 변경했으면 변경된 내용을 Mosquitto에 적용하기 위해 서비스를 다시 시작합니다.

sudo systemctl restart mosquitto

Mosquitto가 정상적으로 다시 시작되었는지 다음 명령으로 확인합니다.

sudo systemctl status mosquitto

실행 결과에서 다음과 같이 표시되면 Mosquitto MQTT Broker가 정상적으로 실행되고 있는 것입니다.

Active: active (running)

또한 상태 화면의 로그에서 다음과 같은 내용을 확인할 수 있습니다.

Loading config file /etc/mosquitto/conf.d/local.conf
Started mosquitto.service - Mosquitto MQTT Broker.

이는 Mosquitto가 앞에서 작성한 local.conf 설정 파일을 읽고 Broker 서비스를 정상적으로 시작했다는 의미입니다.

Loaded 항목에 enabled가 표시되어 있다면 Orange Pi가 부팅될 때 Mosquitto 서비스가 자동으로 시작되도록 설정되어 있는 것도 확인할 수 있습니다.

필요한 경우 다음 명령으로 자동 시작을 설정할 수 있습니다.

sudo systemctl enable mosquitto

이제 Orange Pi의 Mosquitto는 TCP 1883번 포트에서 외부 MQTT Client의 연결을 받을 수 있는 Broker로 동작할 준비가 되었습니다.

다음 절에서는 mosquitto_pubmosquitto_sub 명령을 이용하여 실제로 MQTT 메시지를 Publish하고 Subscribe하면서 Broker의 동작을 확인해 보겠습니다.

MQTT Publish / Subscribe 테스트

Mosquitto MQTT Broker의 설정과 실행 상태를 확인했으므로 이제 실제로 MQTT 메시지를 Publish하고 Subscribe하여 Broker가 정상적으로 메시지를 전달하는지 확인해 보겠습니다.

MQTT 통신에서는 메시지를 보내는 쪽을 Publisher, 메시지를 받는 쪽을 Subscriber​라고 합니다. Publisher와 Subscriber가 서로 직접 통신하는 것이 아니라 중간의 MQTT Broker를 통해 메시지가 전달됩니다.

또한 MQTT에서는 메시지를 구분하기 위해 Topic을 사용합니다.

이번 테스트에서는 다음과 같은 Topic을 사용하겠습니다.

test/topic

먼저 VS Code에서 Terminal을 하나 열고 다음 명령을 실행하여 Subscriber를 실행합니다.

mosquitto_sub -h localhost -t test/topic

각 옵션의 의미는 다음과 같습니다.

  • mosquitto_sub : MQTT 메시지를 Subscribe하는 Client 프로그램
  • -h localhost : 접속할 MQTT Broker의 주소
  • -t test/topic : Subscribe할 MQTT Topic

여기서 localhost현재 명령을 실행하고 있는 Orange Pi 자신을 의미합니다. 따라서 이 명령은 Orange Pi에서 실행 중인 Mosquitto Broker의 test/topic을 Subscribe합니다.

명령을 실행하면 특별한 메시지가 나타나지 않고 입력 대기 상태처럼 보입니다. 실제로는 Broker에 연결된 후 test/topic으로 메시지가 들어오기를 기다리고 있는 상태입니다.

이 상태에서 VS Code의 Terminal을 하나 더 열고 다음 명령을 실행합니다.

mosquitto_pub -h localhost -t test/topic -m "Hello MQTT"

여기서 mosquitto_pub은 MQTT 메시지를 Publish하는 Client 프로그램이며, -m은 전송할 메시지를 지정합니다.

위 명령은 test/topic이라는 Topic으로 다음 메시지를 Publish합니다.

Hello MQTT

Publisher가 메시지를 전송하면 Mosquitto Broker가 메시지를 받아 같은 Topic을 Subscribe하고 있는 Subscriber에게 전달합니다.

따라서 앞에서 실행해 둔 Subscriber Terminal에 다음과 같이 메시지가 표시됩니다.

Hello MQTT

이 과정을 구조로 나타내면 다음과 같습니다.

Subscriber 실행
   mosquitto_sub -h localhost -t test/topic
          │
          │ Subscribe : test/topic
          ▼
   Mosquitto MQTT Broker
          │
          │ 메시지 대기

Publisher 실행
   mosquitto_pub -h localhost -t test/topic -m "Hello MQTT"
          │
          │ Publish
          │ Topic : test/topic
          ▼
   Mosquitto MQTT Broker
          │
          │ test/topic 구독자에게 전달
          ▼
   Subscriber
          │
          ▼
      Hello MQTT

이를 통해 Orange Pi에서 실행되고 있는 Mosquitto MQTT Broker가 Publisher로부터 메시지를 받아 Subscriber에게 정상적으로 전달하고 있음을 확인할 수 있습니다.

현재 테스트에서는 Publisher, Broker, Subscriber가 모두 같은 Orange Pi 안에서 실행되고 있으므로 localhost를 사용하였습니다.

하지만 실제 IoT 시스템에서는 Publisher와 Subscriber가 반드시 같은 컴퓨터에 있을 필요는 없습니다. PC나 ESP32와 같은 다른 장치에서도 Orange Pi의 IP 주소를 지정하여 Mosquitto Broker에 접속할 수 있습니다.

예를 들어 Orange Pi의 IP 주소가 192.168.0.7이라면 다른 장치에서는 다음과 같이 Broker의 주소를 지정할 수 있습니다.

192.168.0.7

이것이 앞에서 local.conflistener 1883을 설정하여 외부 MQTT Client의 접속을 받을 수 있도록 한 이유입니다.

이번 테스트를 통해 Publish → Broker → Subscribe라는 MQTT의 기본적인 메시지 전달 과정을 확인하였습니다.

이제 Orange Pi에서 Mosquitto MQTT Broker의 기본 환경 구축이 완료되었습니다. 다음 장에서는 FastAPI 서버를 구축하고 HTTP를 이용하여 Orange Pi의 기능을 호출할 수 있는 환경을 만들어 보겠습니다.

FastAPI 서버 구축

Python과 가상환경 준비

Mosquitto MQTT Broker의 기본 동작을 확인했으므로 이제 Orange Pi에 FastAPI 서버 환경을 구축해 보겠습니다.

FastAPI는 Python으로 작성된 Web API Framework입니다. HTTP 요청을 받아 처리할 수 있으므로 PC나 웹 애플리케이션에서 Orange Pi의 기능을 호출하거나, 이후 MQTT와 연동하여 IoT 장치를 제어하는 인터페이스로 사용할 수 있습니다.

먼저 Python이 설치되어 있는지 확인합니다.

VS Code의 Terminal에서 다음 명령을 실행합니다.

python3 --version

정상적으로 설치되어 있다면 현재 설치된 Python의 버전이 표시됩니다.

FastAPI를 시스템 전체 Python 환경에 바로 설치할 수도 있지만, 이번 실습에서는 Python 가상환경(Virtual Environment)​을 만들어 사용하겠습니다.

가상환경을 사용하면 프로젝트에서 사용하는 Python 패키지를 시스템의 다른 Python 프로그램과 분리하여 관리할 수 있습니다. 따라서 FastAPI나 MQTT Client 라이브러리 등을 설치하더라도 다른 프로젝트에 영향을 주지 않고 필요한 버전을 독립적으로 관리할 수 있습니다.

먼저 FastAPI 프로젝트를 저장할 디렉터리를 만듭니다.

mkdir fastapi_test
cd fastapi_test

이제 다음 명령으로 Python 가상환경을 생성합니다.

python3 -m venv venv

명령이 완료되면 현재 디렉터리에 venv라는 가상환경 디렉터리가 생성됩니다.

가상환경을 사용하려면 다음 명령으로 활성화합니다.

source venv/bin/activate

가상환경이 정상적으로 활성화되면 Terminal의 프롬프트 앞에 다음과 같이 (venv)가 표시됩니다.

(venv) orangepi@orangepione:~/fastapi_test$

아래는 가상환경이 활성화 된 Terminal 화면입니다.

이 상태에서 pip를 이용하여 설치하는 Python 패키지는 시스템 전체가 아니라 현재 venv 가상환경 안에 설치됩니다.

가상환경을 종료하려면 다음 명령을 사용합니다.

deactivate

이번 FastAPI 실습에서는 작업을 시작할 때 fastapi_test 디렉터리로 이동한 후 가상환경을 활성화하여 사용하겠습니다.

다음 절에서는 이 가상환경에 FastAPI와 Uvicorn을 설치하고 간단한 API 서버를 만들어 보겠습니다.

FastAPI 설치

앞 절에서 Python 가상환경을 준비했으므로 이제 FastAPI와 서버 실행에 필요한 Uvicorn을 설치하겠습니다.

먼저 fastapi_test 디렉터리로 이동하고 가상환경을 활성화합니다.

cd ~/fastapi_test
source venv/bin/activate
가상환경이 정상적으로 활성화되면 Terminal의 프롬프트 앞에 (venv)가 표시됩니다.
(venv) orangepi@orangepione:~/fastapi_test$
이제 Python의 패키지 관리 프로그램인 pip를 이용하여 FastAPI와 Uvicorn을 설치합니다.
pip install fastapi uvicorn
여기서 FastAPI는 HTTP 요청을 받아 처리하는 Web API를 작성하기 위한 Python Framework이며, Uvicorn은 작성한 FastAPI 애플리케이션을 실제 Web Server로 실행하기 위해 사용하는 ASGI Server입니다.

즉, 두 프로그램의 역할을 간단하게 나타내면 다음과 같습니다.

FastAPI
   │
   │ API 작성
   ▼
main.py
   │
   │ 실행
   ▼
Uvicorn
   │
   │ HTTP
   ▼
PC / Web Browser
이번 Orange Pi에는 이미 FastAPI와 Uvicorn을 설치하여 사용하고 있으므로 다시 설치하지 않고, 현재 가상환경에 설치되어 있는 패키지를 확인해 보겠습니다.

먼저 가상환경이 활성화된 상태에서 다음 명령을 실행합니다.

pip list

설치된 Python 패키지 목록에서 fastapiuvicorn을 확인할 수 있습니다.

또는 다음과 같이 필요한 패키지만 확인할 수도 있습니다.

pip show fastapi
pip show uvicorn

각 명령을 실행하면 설치된 버전과 설치 위치 등의 정보를 확인할 수 있습니다.

중요한 점은 이 패키지들이 Orange Pi의 시스템 Python 전체에 설치된 것이 아니라, 앞에서 만든 fastapi_test/venv 가상환경 안에 설치되어 있다는 것입니다.

따라서 이후 FastAPI 서버를 실행할 때도 먼저 가상환경을 활성화한 상태에서 작업합니다.

이제 Orange Pi에서 FastAPI 프로그램을 작성하고 실행할 준비가 되었습니다.

다음 절에서는 main.py 파일을 만들고 간단한 FastAPI 서버를 작성해 보겠습니다.

간단한 FastAPI 서버 작성

FastAPI와 Uvicorn 설치가 완료되었으므로 이제 FastAPI 서버 프로그램을 살펴보겠습니다.

이번 실습에서는 이후 Mosquitto MQTT Broker와 연결하기 위해 main.py에 FastAPI와 MQTT 기능을 함께 구성하였습니다. 먼저 이 절에서는 FastAPI와 관련된 부분을 중심으로 살펴보고, MQTT와 연결되는 부분은 뒤에서 다시 설명하겠습니다.

VS Code의 Remote – SSH 환경에서 fastapi_test 디렉터리의 main.py 파일을 작성합니다.

먼저 FastAPI를 사용하기 위해 다음과 같이 모듈을 가져오고 애플리케이션 객체를 생성합니다.

from fastapi import FastAPI

app = FastAPI()

FastAPI()를 이용하여 생성한 app 객체가 이번 프로그램의 FastAPI 애플리케이션입니다.

이제 HTTP 요청에 응답할 API를 정의합니다.

가장 간단한 API는 다음과 같습니다.

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

@app.get("/")은 HTTP의 GET 방식으로 / 경로에 요청이 들어오면 바로 아래의 root() 함수를 실행하도록 지정합니다.

root() 함수에서는 Python의 Dictionary 형태로 데이터를 반환합니다.

{"message": "Hello FastAPI"}

FastAPI는 이를 JSON 형식의 HTTP Response로 변환하여 Client에 전달합니다.

따라서 이후 PC의 Web Browser에서 Orange Pi의 IP 주소와 FastAPI의 포트를 이용하여 접속하면 다음과 같은 결과를 확인할 수 있습니다.

http://192.168.0.7:8000/
{
    "message": "Hello FastAPI"
}

이번 main.py에는 센서 값을 확인하기 위한 API도 작성되어 있습니다.

@app.get("/soil")
def soil():
    return {
        "sensor": 1,
        "moisture": soil_moisture
    }

/soil 경로로 GET 요청을 보내면 현재 저장되어 있는 토양 수분 값인 soil_moisture를 JSON 형식으로 반환합니다.

즉 다음과 같은 구조입니다.

GET /soil
    │
    ▼
soil()
    │
    ▼
soil_moisture
    │
    ▼
JSON Response

현재 soil_moisture의 초기값은 다음과 같이 0으로 설정되어 있습니다.

soil_moisture = 0

따라서 아직 MQTT를 통해 센서 데이터가 들어오지 않았다면 /soil API에서는 초기값인 0이 반환됩니다.

main.py에는 다음과 같이 밸브를 제어하기 위한 POST API도 작성되어 있습니다.

@app.post("/valve/1/on")
def valve_on():
    mqtt_client.publish("garden/valve/1/set", "ON")

    return {
        "valve": 1,
        "state": "ON"
    }

여기서는 HTTP POST 요청을 사용합니다.

다만 valve_on() 함수 내부에는 Mosquitto를 통해 MQTT 메시지를 Publish하는 코드가 포함되어 있습니다. 이 부분은 FastAPI와 Mosquitto가 실제로 연결되는 부분이므로 뒤의 「FastAPI와 Mosquitto 연결」 장에서 자세히 살펴보겠습니다.

현재 단계에서는 FastAPI를 이용하면 URL의 경로와 Python 함수를 연결하여 다음과 같은 API를 만들 수 있다는 점을 확인하면 됩니다.

  • GET / : FastAPI 서버의 기본 응답 확인
  • GET /soil : 토양 수분 값 확인
  • POST /valve/1/on : 밸브 ON 제어 요청

이처럼 FastAPI는 Orange Pi 내부의 Python 프로그램과 PC 또는 Web Client 사이에 HTTP 기반의 인터페이스를 만들어 줍니다.

하지만 main.py 파일을 작성하는 것만으로는 외부에서 이 API에 접속할 수 없습니다. FastAPI 애플리케이션을 실제 Web Server로 실행해야 합니다.

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

다음 절에서는 Uvicorn을 이용하여 현재의 main.py를 실행해 보겠습니다.

Uvicorn으로 서버 실행

main.py 작성이 완료되었으면 이제 Uvicorn을 이용하여 FastAPI 서버를 실행하겠습니다.

먼저 VS Code의 Terminal에서 fastapi_test 디렉터리로 이동하고 Python 가상환경을 활성화합니다.

cd ~/fastapi_test
source venv/bin/activate

Terminal의 프롬프트 앞에 (venv)가 표시되는지 확인합니다.

(venv) orangepi@orangepione:~/fastapi_test$

이제 다음 명령으로 FastAPI 서버를 실행합니다.

uvicorn main:app --host 0.0.0.0 --port 8000

여기서 각 항목의 의미는 다음과 같습니다.

main:appmain.py 파일에 정의되어 있는 app 객체를 실행한다는 의미입니다.

app = FastAPI()

--host 0.0.0.0은 Orange Pi의 특정 네트워크 인터페이스에만 제한하지 않고 외부에서 FastAPI 서버에 접속할 수 있도록 합니다.

--port 8000은 FastAPI 서버가 TCP 8000번 포트에서 HTTP 연결을 기다리도록 설정합니다.

Uvicorn을 실행하면 main.py가 로드되고 FastAPI 애플리케이션이 시작됩니다.

이번 main.py에는 MQTT Client 코드도 포함되어 있으므로 이 과정에서 다음 코드 역시 실행됩니다.

mqtt_client.connect("127.0.0.1", 1883, 60)
mqtt_client.loop_start()

127.0.0.1은 Orange Pi 자신을 의미하므로, main.py의 MQTT Client는 같은 Orange Pi에서 실행되고 있는 Mosquitto MQTT Broker의 1883번 포트에 연결됩니다.

Broker 연결이 완료되면 앞에서 작성한 on_connect() Callback이 호출됩니다.

def on_connect(client, userdata, flags, reason_code, properties=None):
    print("MQTT connected:", reason_code)
    client.subscribe("garden/soil/1")

따라서 Uvicorn으로 main.py를 실행하면 FastAPI 서버가 시작되는 것과 함께 MQTT Client도 Mosquitto에 연결되고 garden/soil/1 Topic을 Subscribe하게 됩니다.

전체 실행 과정을 간단히 나타내면 다음과 같습니다.

Uvicorn 실행
     │
     ▼
main.py 로드
     │
     ├───────────────┐
     ▼               ▼
FastAPI          MQTT Client
     │               │
Port 8000             │ Connect
HTTP                  ▼
                  Mosquitto
                  Port 1883
                      │
                      ▼
              Subscribe
              garden/soil/1

정상적으로 실행되면 Terminal에서 Uvicorn의 실행 메시지와 함께 MQTT 연결 메시지를 확인할 수 있습니다.

MQTT connected: Success

또한 Uvicorn이 정상적으로 실행되면 서버가 8000번 포트에서 HTTP 요청을 기다리는 상태가 됩니다.

이 상태에서는 Terminal이 입력 대기 상태로 돌아오지 않습니다. 이는 Uvicorn 서버가 계속 실행되면서 HTTP 요청을 기다리고 있기 때문입니다.

서버를 종료하려면 Terminal에서 Ctrl + C를 입력합니다.

이제 Orange Pi에서는 다음 두 가지 서버 기능이 함께 동작하게 됩니다.

Mosquitto : MQTT Broker – TCP 1883

FastAPI + Uvicorn : Web API – TCP 8000

아래는 uvicorn이 실행된 Terminal 화면입니다.

다음 장에서는 PC의 Web Browser에서 Orange Pi의 FastAPI 서버에 실제로 접속하여 API의 동작을 확인해 보겠습니다.

FastAPI와 Mosquitto 연결

main.py의 FastAPI와 MQTT 연결 구조

앞에서 FastAPI와 Mosquitto MQTT Broker가 각각 정상적으로 동작하는 것을 확인하였습니다.

이제 두 기능을 연결해 보겠습니다.

Mosquitto와 FastAPI가 서로 직접 연결되는 것은 아닙니다. 이번 실습에서는 FastAPI를 실행하는 main.py 안에 MQTT Client 기능을 함께 구현하여 Mosquitto Broker와 연결합니다.

이를 위해 main.py에서는 FastAPI와 함께 Paho MQTT Client 라이브러리를 사용합니다.

from fastapi import FastAPI
import paho.mqtt.client as mqtt

app = FastAPI()

soil_moisture = 0

FastAPI는 PC나 Web Client에서 들어오는 HTTP 요청을 처리하고, paho.mqtt.client는 Mosquitto MQTT Broker와 메시지를 주고받는 역할을 합니다.

main.py 하나가 HTTP와 MQTT 사이를 연결하는 역할을 하게 됩니다.

MQTT Broker 연결

먼저 MQTT Client를 생성합니다.

mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)

그리고 MQTT 연결 및 메시지 수신 Callback을 등록합니다.

mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

이후 다음 코드에서 Mosquitto Broker에 연결합니다.

mqtt_client.connect("127.0.0.1", 1883, 60)
mqtt_client.loop_start()

127.0.0.1은 Orange Pi 자신을 의미합니다. 따라서 main.py의 MQTT Client는 같은 Orange Pi에서 실행되고 있는 Mosquitto의 1883번 포트에 연결됩니다.

loop_start()는 MQTT 통신을 백그라운드에서 처리하도록 합니다. 이 때문에 FastAPI가 HTTP 요청을 처리하는 동안에도 MQTT 메시지를 받을 수 있습니다.

MQTT Topic Subscribe

Mosquitto와 연결되면 on_connect() Callback이 호출됩니다.

def on_connect(client, userdata, flags, reason_code, properties=None):
    print("MQTT connected:", reason_code)
    client.subscribe("garden/soil/1")

여기에서 garden/soil/1 Topic을 Subscribe합니다.

따라서 이후 ESP32나 다른 MQTT Publisher가 garden/soil/1으로 토양 수분 값을 Publish하면 Mosquitto Broker가 해당 메시지를 main.py의 MQTT Client에 전달합니다.

메시지가 들어오면 on_message() Callback이 실행됩니다.

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

    if msg.topic == "garden/soil/1":
        payload = msg.payload.decode()

        try:
            soil_moisture = int(payload)
        except ValueError:
            print("Invalid soil moisture value:", repr(payload))

수신한 MQTT Payload를 정수로 변환하여 soil_moisture 변수에 저장합니다.

FastAPI에서는 이 값을 /soil API를 통해 외부에 제공합니다.

@app.get("/soil")
def soil():
    return {
        "sensor": 1,
        "moisture": soil_moisture
    }

따라서 데이터가 이동하는 방향은 다음과 같습니다.

MQTT Publisher
      │
      │ garden/soil/1
      ▼
Mosquitto Broker
      │
      ▼
main.py MQTT Client
      │
      ▼
soil_moisture
      │
      ▼
FastAPI GET /soil
      │
      ▼
PC / Web

반대 방향의 제어도 가능합니다.

main.py에는 다음과 같이 밸브를 ON하기 위한 FastAPI가 작성되어 있습니다.

@app.post("/valve/1/on")
def valve_on():
    mqtt_client.publish("garden/valve/1/set", "ON")

    return {
        "valve": 1,
        "state": "ON"
    }

PC에서 /valve/1/on으로 HTTP POST 요청이 들어오면 FastAPI의 valve_on() 함수가 실행됩니다.

이 함수에서는 MQTT Client를 이용하여 garden/valve/1/set Topic으로 ON 메시지를 Publish합니다.

따라서 이번에는 데이터의 흐름이 반대가 됩니다.

PC / Web
    │
    │ HTTP POST
    ▼
FastAPI
    │
    │ valve_on()
    ▼
main.py MQTT Client
    │
    │ Publish
    │ garden/valve/1/set : ON
    ▼
Mosquitto Broker
    │
    ▼
MQTT Subscriber

결국 main.pyMQTT → HTTP 방향과 HTTP → MQTT 방향을 모두 연결합니다.

센서 데이터는 MQTT를 통해 Orange Pi로 들어와 FastAPI를 통해 외부에서 확인할 수 있고, 반대로 FastAPI를 통해 받은 제어 요청은 MQTT 메시지로 변환하여 IoT 장치에 전달할 수 있습니다.

이것이 이번 실습에서 FastAPI와 Mosquitto를 연결하는 핵심 구조입니다.

MQTT 센서 데이터를 FastAPI에서 확인

우리는 아직 ESP32와는 연결이 되지 않았으므로 ESP32를 대신하여 PC에서 MQTT를 시험 해 볼 수 있는 MQTT Explorer를 사용합니다. 인터텟에서 다운로드 하여 PC에 설치하면 됩니다.

Protocal을 mqtt://, Host를 192.168.0.7 그리고 Port를 1883으로 지정한 후 CONNECT를 누르면 접속이 되고 아래와 같은 화면이 나타납니다.

Topic에 garden/soil/1을 입력하고 바로 밑에 raw를 선택한 다음 입력창에 66을 입력합니다.

이것은 MQTT explorer가 garden/soil/1 토픽에 66을 Publish하겠다는 의미 입니다.

오른쪽 PUBLISH 버튼을 누르면 발행이 되고 웹페이지에서 발행이 된 것을 확인 합니다.

웹브라우저의 주소 입력난에 http://192.168.0.7:8000/soil 을 입력합니다.

다음과 같이 표시 됩니다.

Web Browser에서 다음과 같이 moisture 값이 66으로 표시되는 것을 확인할 수 있습니다.

{"sensor":1,"moisture":66}
이 숫자는 앞에서 MQTT Explorer를 이용하여 garden/soil/1 Topic으로 Publish한 값입니다.

즉, MQTT Explorer에서 전송한 66이 Mosquitto MQTT Broker를 거쳐 main.pyon_message() Callback으로 전달되었고, 다음 코드에 의해 soil_moisture 변수에 저장되었습니다.

soil_moisture = int(payload)
그리고 Web Browser에서 /soil API를 요청하면 FastAPI가 현재 저장되어 있는 soil_moisture 값을 반환합니다.

전체 데이터 흐름을 간단히 나타내면 다음과 같습니다.

MQTT Explorer
     │
     │ Publish
     │ garden/soil/1 : 66
     ▼
Mosquitto MQTT Broker
     │
     ▼
main.py
     │
     │ on_message()
     ▼
soil_moisture = 66
     │
     ▼
FastAPI GET /soil
     │
     ▼
Web Browser
{"sensor":1,"moisture":66}
이를 통해 Mosquitto MQTT Broker와 FastAPI가 main.py를 통해 정상적으로 연결되어 있으며, MQTT로 수신한 데이터를 HTTP API를 통해 확인할 수 있음을 확인하였습니다.

현재는 ESP32 대신 MQTT Explorer에서 센서 값을 전송했지만, 이후 ESP32가 garden/soil/1 Topic으로 실제 토양 수분 값을 Publish하도록 하면 동일한 구조를 그대로 사용할 수 있습니다.

다음에는 반대 방향으로 FastAPI에서 제어 명령을 실행하고 MQTT 메시지가 Publish되는 과정을 확인해 보겠습니다.

FastAPI에서 MQTT Publish

앞 절에서는 MQTT Explorer에서 garden/soil/1 Topic으로 센서 값을 Publish하고, FastAPI의 /soil API를 통해 해당 값을 확인하였습니다.

이번에는 반대 방향의 통신을 확인해 보겠습니다.

즉, PC에서 FastAPI로 제어 명령을 보내고, FastAPI가 이를 MQTT 메시지로 Publish하는 과정을 시험합니다.

이번 실습에서는 실제 ESP32가 아직 연결되어 있지 않으므로 MQTT Explorer를 이용하여 FastAPI에서 Publish한 MQTT 메시지를 확인하겠습니다.

main.py에는 다음과 같이 밸브를 ON하기 위한 API가 작성되어 있습니다.

@app.post("/valve/1/on")
def valve_on():
    mqtt_client.publish("garden/valve/1/set", "ON")

    return {
        "valve": 1,
        "state": "ON"
    }

@app.post("/valve/1/on")/valve/1/on으로 HTTP POST 요청이 들어오면 valve_on() 함수를 실행하도록 합니다.

함수가 실행되면 다음 코드에 의해 MQTT 메시지가 Publish됩니다.

mqtt_client.publish("garden/valve/1/set", "ON")
여기서 MQTT Topic과 Payload는 다음과 같습니다.
Topic   : garden/valve/1/set
Payload : ON

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

PC
 │
 │ HTTP POST
 │ /valve/1/on
 ▼
FastAPI
 │
 │ valve_on()
 ▼
mqtt_client.publish()
 │
 │ Topic   : garden/valve/1/set
 │ Payload : ON
 ▼
Mosquitto MQTT Broker
 │
 ▼
MQTT Explorer

이제 실제 동작을 확인해 보겠습니다.

먼저 PC에서 MQTT Explorer를 실행하여 앞 절과 동일하게 Orange Pi의 Mosquitto MQTT Broker에 접속합니다.

MQTT Explorer는 Broker에서 전달되는 MQTT 메시지를 확인할 수 있으므로, 이후 FastAPI에서 garden/valve/1/set으로 Publish되는 ON 메시지를 확인하는 데 사용합니다.

다음으로 FastAPI의 API를 실행합니다.

/valve/1/on은 GET이 아니라 POST API이므로 Web Browser의 주소창에 URL을 입력하는 방법보다는 FastAPI에서 제공하는 Swagger UI를 이용하면 편리합니다.

Web Browser에서 다음 주소로 접속합니다.

http://192.168.0.7:8000/docs
Swagger UI에는 main.py에 작성한 FastAPI의 API 목록이 자동으로 표시됩니다.

여기에서 다음 POST API를 선택합니다.

POST /valve/1/on
Try it out을 누른 다음 Execute를 실행합니다.

그러면 FastAPI에서 valve_on() 함수가 실행되고 Mosquitto Broker로 다음 MQTT 메시지가 Publish됩니다.

garden/valve/1/set : ON
동시에 Swagger UI에서는 FastAPI가 반환한 다음 JSON 응답을 확인할 수 있습니다.
{
    "valve": 1,
    "state": "ON"
}

이제 MQTT Explorer를 확인합니다.

FastAPI에서 Publish한 메시지가 정상적으로 Mosquitto Broker에 전달되었다면 garden/valve/1/set Topic에서 다음 값을 확인할 수 있습니다.

ON
이로써 앞 절과 반대 방향의 통신도 확인할 수 있습니다.

앞 절에서는

MQTT Explorer → Mosquitto → FastAPI → Web Browser

방향으로 센서 데이터를 확인하였고, 이번에는

Swagger UI → FastAPI → Mosquitto → MQTT Explorer

방향으로 제어 명령을 확인하였습니다.

현재는 MQTT Explorer가 ESP32를 대신하고 있지만, 이후 ESP32에서 garden/valve/1/set Topic을 Subscribe하도록 구현하면 FastAPI에서 보낸 ON 메시지를 ESP32가 수신하여 실제 밸브를 제어하는 구조로 확장할 수 있습니다.

따라서 이번 실습을 통해 Orange Pi의 FastAPI와 Mosquitto MQTT Broker 사이에서 센서 데이터 수신과 제어 명령 송신의 양방향 연결이 정상적으로 동작하는 것을 확인할 수 있습니다.

결론

이번 글에서는 Orange Pi One에 Armbian Linux를 설치하고, Mosquitto MQTT Broker와 FastAPI를 이용하여 간단한 IoT 서버 환경을 구축해 보았습니다.

처음에는 모니터와 키보드를 연결하여 Linux의 부팅과 초기 설정을 진행하였지만, 이후에는 SSH와 VS Code Remote – SSH를 이용하여 Windows PC에서 Orange Pi를 원격으로 관리하고 개발할 수 있는 환경을 구성하였습니다.

Mosquitto에서는 MQTT의 Publish / Subscribe 동작을 확인하였으며, FastAPI와 연결하여 MQTT와 HTTP 사이에서 데이터를 전달하는 과정도 시험하였습니다.

센서 데이터 방향에서는 MQTT Explorer가 garden/soil/1 Topic으로 전송한 값을 FastAPI의 /soil API를 통해 Web Browser에서 확인하였습니다. 반대로 제어 방향에서는 FastAPI의 API를 실행하여 garden/valve/1/set Topic으로 MQTT 메시지를 Publish하고 MQTT Explorer에서 이를 확인하였습니다.

따라서 이번 실습을 통해 다음과 같은 양방향 IoT 서버의 기본 구조를 확인할 수 있었습니다.

Sensor → MQTT → Mosquitto → FastAPI → HTTP → PC

PC → HTTP → FastAPI → Mosquitto → MQTT → Device

아직 실제 ESP32를 연결하지 않고 MQTT Explorer를 이용하여 시험하였지만, 이를 통해 Orange Pi의 서버 부분이 정상적으로 동작하는 것을 먼저 확인할 수 있었습니다.

다음 실습에서는 MQTT Explorer가 담당했던 역할을 ESP32가 직접 수행하도록 구성하여, ESP32와 Orange Pi 사이에서 실제 MQTT 통신을 구현해 보겠습니다.

댓글 남기기