[실습] 웹에서 라즈베리파이 LED 유뮤선 제어(macOS)
필기자
2026-05-07 14:59
14
0
본문
웹에서 라즈베리파이 LED 유무선 제어
목적
목차
1. 전체 시스템 이해 및 시스템 설치
2. 프론트엔드 및 서버프로그램 개발
※ Raspberry Pi 기반 목업(mock-up) 시스템이므로 macOS에 파이썬 가상환경 구성
목적
- 아파치 웹서버와 파이썬 Socket.IO 서버를 구성한다.
- 내 PC(macOS)에서 웹 브라우저(크롬)로 라즈베리파이 LED를 제어한다.
목차
1. 전체 시스템 이해 및 시스템 설치
2. 프론트엔드 및 서버프로그램 개발
2. 프론트엔드 및 서버프로그램 개발
1. 전체 시스템 이해 및 시스템 설치
- 구성요소
- 브라우저 (Chrome)
- 사용자가 시스템에 접속하는 클라이언트 도구임.
- Apache 웹 서버에 접속해 프론트엔드 프로그램을 로드함.
- 브라우저는 사용자가 웹 주소를 입력할 때 처음 실행되는 프로그램으로, HTML/CSS/JS로 구성된 화면을 보여주는 역할을 함.
- 사용자가 시스템에 접속하는 클라이언트 도구임.
- Apache 웹 서버 (포트 80)
- HTML, CSS, JavaScript 등 정적 리소스를 브라우저에 제공함.
- 실시간 통신은 담당하지 않음.
- Apache는 가장 널리 사용되는 오픈소스 웹 서버로, 정적 파일을 브라우저에 전달해 웹페이지를 구성하는 역할을 함.
- Python Socket.IO 서버 (포트 5000)
- 웹소켓 기반의 실시간 양방향 통신을 처리함.
- 프론트엔드의 LED 제어 요청 및 상태 응답을 처리함.
- Socket.IO는 WebSocket을 이용해 서버와 클라이언트 간 실시간 데이터를 빠르게 주고받도록 도와주는 라이브러리임.
- 브라우저 (Chrome)
- 동작 흐름
- 브라우저가 Apache 서버에 HTTP 요청을 보냄.
- Apache 서버가 프론트엔드 프로그램을 브라우저에 제공함.
- 프론트엔드는 Socket.IO 서버와 WebSocket 연결을 생성함.
- 사용자 동작에 따라 LED 제어 명령을 서버에 전송함.
- 서버는 GPIO를 통해 하드웨어를 제어하고 상태를 응답함.
- 구성 이유
- 정적 콘텐츠와 동적 통신 분리
- Apache는 HTML, CSS, JS 같은 정적인 파일만 제공하므로 요청 처리 속도가 빠르고 안정적임.
- 실시간 통신은 별도의 Socket.IO 서버가 담당하여 처리 부담을 분산함.
- 역할별 모듈화
- 웹 서버와 실시간 서버를 분리하면 유지보수와 확장에 유리함.
- 각 모듈의 기능이 명확하게 분리되어 구조가 단순하고 이해하기 쉬움.
- 실시간 제어에 적합한 구조
- LED 같은 물리적 장치는 빠른 반응이 필요하므로 WebSocket 기반 통신이 적합함.
- Socket.IO는 실시간 이벤트 기반 구조로 지연 없이 상태 전송이 가능함.
- 개발 및 테스트 용이
- 프론트엔드는 Apache로 언제든 새로고침 테스트 가능하고,
- 서버 로직은 Python 단에서 독립적으로 개발 및 디버깅 가능함.
- 정적 콘텐츠와 동적 통신 분리
- macOS에서 Apache 서버 활성화
- macOS는 Apache가 기본 내장되어 있어 별도 설치(Homebrew 등) 불필요
- apachectl 명령으로 시작·중지·재시작·문법검사 제어
- HTTP 응답은 curl로, 프로세스는 ps로, 에러는 /var/log/apache2/error_log로 확인
- DocumentRoot 기본 경로: /Library/WebServer/Documents
- launchctl로 부팅 시 자동 시작 등록/해제 가능
- DocumentRoot 권한을 현재 사용자로 변경해 sudo 없이 파일 편집 가능하게 함
# 아파치 버전 확인
httpd -v
Server version: Apache/2.4.58 (Unix)
Server built: Aug 30 2024 04:27:22
Server built: Aug 30 2024 04:27:22
# 아파치 시작
sudo apachectl start
Password:
httpd: Could not reliably determine the server's fully qualified domain name, using ::1. Set the 'ServerName' directive globally to suppress this message
httpd: Could not reliably determine the server's fully qualified domain name, using ::1. Set the 'ServerName' directive globally to suppress this message
# HTTP 응답 확인 (기본 페이지)
curl http://localhost
<html><body><h1>It works!</h1></body></html>
# 아파치 동작 확인 (프로세스)
ps -ef | grep httpd
0 1234 1 0 9:00AM ?? 0:00.05 /usr/sbin/httpd -D FOREGROUND
70 1235 1234 0 9:00AM ?? 0:00.00 /usr/sbin/httpd -D FOREGROUND
70 1236 1234 0 9:00AM ?? 0:00.00 /usr/sbin/httpd -D FOREGROUND
501 1237 1100 0 9:01AM ttys000 0:00.00 grep httpd
70 1235 1234 0 9:00AM ?? 0:00.00 /usr/sbin/httpd -D FOREGROUND
70 1236 1234 0 9:00AM ?? 0:00.00 /usr/sbin/httpd -D FOREGROUND
501 1237 1100 0 9:01AM ttys000 0:00.00 grep httpd
# 아파치 설정 파일 문법 검사 (재시작 전 권장)
sudo apachectl configtest
Password:
Syntax OK
Syntax OK
# 아파치 재시작 (설정 변경 후)
sudo apachectl restart
httpd: Could not reliably determine the server's fully qualified domain name, using ::1. Set the 'ServerName' directive globally to suppress this message
# 아파치 중지
sudo apachectl stop
# 에러 로그 확인 (최근 10줄)
tail /var/log/apache2/error_log
[Mon Nov 04 09:00:01.123456 2024] [mpm_prefork:notice] [pid 1234] AH00163: Apache/2.4.58 (Unix) configured -- resuming normal operations
[Mon Nov 04 09:00:01.234567 2024] [core:notice] [pid 1234] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
[Mon Nov 04 09:00:01.234567 2024] [core:notice] [pid 1234] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
# 아파치 부팅 시 자동 시작 등록
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
# 아파치 부팅 시 자동 시작 해제
sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist
# 도큐먼트 루트 폴더 소유권 변경
sudo chown -R $(whoami):staff /Library/WebServer/Documents
2. 프론트엔드 및 서버프로그램 개발
- Apache DocumentRoot에 프론트프로그램 개발
- VS Code로 /Library/WebServer/Documents 폴더 전체를 열어 작업 (index.html 외 style.css·app.js 등 추가 파일도 같이 만들기 위함)
- code 명령은 VS Code에서 [Cmd+Shift+P] → 'Shell Command: Install code command in PATH' 활성화 후 사용 가능
- VS Code 사이드바의 'New File' 아이콘으로 index.html 등 파일 생성
- 저장 후 브라우저에서 http://localhost 접속하여 확인
# VS Code로 DocumentRoot 폴더 열기
code /Library/WebServer/Documents/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LED 제어</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
</head>
<body>
<h3>LED 제어</h3>
<label><input type="radio" name="led" value="on"> ON</label>
<label><input type="radio" name="led" value="off"> OFF</label>
<p id="status">LED 상태: 알 수 없음</p>
<script>
const socket = io("http://라즈베리파이_IP:5000");
socket.on("connect", function() {
socket.emit("get_led_status");
});
socket.on("led_status", function(data) {
$("#status").text("LED 상태: " + data.state);
$("input[name='led'][value='" + data.state + "']").prop("checked", true);
});
$("input[name='led']").change(function() {
const state = $(this).val();
socket.emit("led_control", { state: state });
});
</script>
</body>
</html>
※ Raspberry Pi 기반 목업(mock-up) 시스템이므로 macOS에 파이썬 가상환경 구성
- 파이썬 가상환경 설정
- 가상환경 설정을 위해 홈폴더 이동: cd ~
- macOS에는 Python 3가 기본 포함 (Xcode Command Line Tools 또는 Homebrew). 미설치 시 brew install python 또는 python.org에서 설치
- 가상환경 디렉토리 생성 및 이동: mkdir iot && cd iot
- 가상환경 설정: python3 -m venv iot
- 가상환경 활성화: source iot/bin/activate
- 가상환경 비활성화: deactivate
# 홈폴더로 이동
cd ~
# Python 3 설치 확인 (버전 출력되면 정상)
python3 --version
# 가상환경 디렉토리 생성 및 이동
mkdir iot && cd iot
# 가상환경 생성
python3 -m venv iot
# 가상환경 활성화
source iot/bin/activate
# 가상환경 비활성화 (필요할 때만)
deactivate
user@MacBook ~ % python3 --version
Python 3.11.6
Python 3.11.6
- 파이썬 가상환경 환경변수 등록
- 환경변수 등록을 위해 ~/.zshrc 파일 수정 (macOS Catalina+ 기본 셸은 zsh, bash 사용자는 ~/.bash_profile 수정)
- VS Code로 직접 편집
# VS Code로 zshrc 편집
code ~/.zshrc
# 제일 하단에 아래 한 줄 추가
alias iot_='source ~/iot/iot/bin/activate && cd ~/iot/'
# 저장하고 종료
# 환경변수 적용
source ~/.zshrc
# iot_ 명령 입력 시 가상환경 활성화 + 가상환경 폴더로 이동
iot_
- macOS 환경에서 가상 GPIO 사용
- 라즈베리파이 환경이 아닌 macOS 같은 테스트 환경에서 사용
- GPIO 코드 사용 시 에러 대체
- ~/iot 폴더 안에 mockgpio.py 파일 생성 (VS Code로 ~/iot 폴더 열어두면 편리)
# VS Code로 iot 폴더 열기
code ~/iot
# mockgpio.py
class MockGPIO:
BCM = 'BCM'
BOARD = 'BOARD'
OUT = 'OUT'
IN = 'IN'
LOW = 0
HIGH = 1
_pin_states = {}
def setmode(self, mode):
print(f"[MOCK] GPIO mode set to {mode}")
def setup(self, pin, mode):
self._pin_states[pin] = self.LOW
print(f"[MOCK] GPIO pin {pin} set as {mode}")
def output(self, pin, value):
self._pin_states[pin] = value
print(f"[MOCK] GPIO pin {pin} output set to {value}")
def input(self, pin):
value = self._pin_states.get(pin, self.LOW)
print(f"[MOCK] GPIO pin {pin} read as {value}")
return value
def cleanup(self):
self._pin_states.clear()
print("[MOCK] GPIO cleanup complete")
GPIO = MockGPIO()
- iot 가상환경에 Socket.IO 서버 프로그램 개발
- iot_ 명령으로 가상환경 진입 후 pip install flask flask-socketio eventlet 설치
- ~/iot 폴더 안에 iot_socket.py 파일 생성
user@MacBook ~ % iot_
(iot) user@MacBook iot % pip install flask flask-socketio eventlet
from flask import Flask
from flask_socketio import SocketIO
from flask import request
try:
import RPi.GPIO as GPIO
except (ImportError, RuntimeError):
from mockgpio import GPIO # 인스턴스를 직접 가져옴
# 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")
# 현재 LED 상태 반환 함수
def get_led_state():
return "on" if GPIO.input(LED_PIN) else "off"
# 클라이언트로부터 제어 요청 수신
@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}, room=request.sid)
# 초기 상태 요청 처리
@socketio.on("get_led_status")
def handle_status_request():
state = get_led_state()
print(f"led 상태 : { get_led_state()}")
socketio.emit("led_status", {"state": state}, room=request.sid)
# 서버 실행
if __name__ == "__main__":
socketio.run(app, host="0.0.0.0", port=5000)
댓글목록0