웹에서 라즈베리파이 LED 유뮤선 제어 > 스마트기기시스템

본문 바로가기

[실습] 웹에서 라즈베리파이 LED 유뮤선 제어

필기자
2025-05-05 20:51 1,271 0

본문

웹에서 라즈베리파이 LED 유뮤선 제어

목적
  • 아파치웹서버와 파이썬 Soket.io 서버를 구성한다.
  • 내 PC에서 웹 프라우져(크롬)으로 라즈베리파이 LED를 제어한

목차
1. 전체 시스템 이해 및 시스템 설치
2. 프론트엔드 및 서버프로그램 개발

1. 전체 시스템 이해 및 시스템 설치
  • 구성요소
    • 브라우저 (Chrome)
      • 사용자가 시스템에 접속하는 클라이언트 도구임.
        • Apache 웹 서버에 접속해 프론트엔드 프로그램을 로드함.
        • 브라우저는 사용자가 웹 주소를 입력할 때 처음 실행되는 프로그램으로, HTML/CSS/JS로 구성된 화면을 보여주는 역할을 함.
    • Apache 웹 서버 (포트 80)
      • HTML, CSS, JavaScript 등 정적 리소스를 브라우저에 제공함.
      • 실시간 통신은 담당하지 않음.
      • Apache는 가장 널리 사용되는 오픈소스 웹 서버로, 정적 파일을 브라우저에 전달해 웹페이지를 구성하는 역할을 함.
    • Python Socket.IO 서버 (포트 5000)
      • 웹소켓 기반의 실시간 양방향 통신을 처리함.
      • 프론트엔드의 LED 제어 요청 및 상태 응답을 처리함.
      • Socket.IO는 WebSocket을 이용해 서버와 클라이언트 간 실시간 데이터를 빠르게 주고받도록 도와주는 라이브러리임.

20250505220247_653372e8733b8d407203ea47dd234b2f_8gtu.png
  • 동작 흐름
    • 브라우저가 Apache 서버에 HTTP 요청을 보냄.
    • Apache 서버가 프론트엔드 프로그램을 브라우저에 제공함.
    • 프론트엔드는 Socket.IO 서버와 WebSocket 연결을 생성함.
    • 사용자 동작에 따라 LED 제어 명령을 서버에 전송함.
    • 서버는 GPIO를 통해 하드웨어를 제어하고 상태를 응답함.
20250505222731_653372e8733b8d407203ea47dd234b2f_qoyi.png

//https://mermaid.live/

   %%{init: {'themeVariables': {
  'fontSize': '18px',
  'clusterBkg': '#FFFFFF',
  'clusterBorder': '#333333',
  'clusterFontSize': '22px',
  'nodeSpacing': 50,
  'rankSpacing': 80
}}}%%
flowchart TB
    %% 노드 정의
    Browser["Chrome 브라우저"]
    Frontend["프론트엔드 프로그램<br>(HTML, CSS, JavaScript)"]
   
   
    Apache["Apache 웹 서버<br>(포트 80)"]
    SocketIO["Python Socket.IO<br>서버(포트 5000)"]
    Hardware["하드웨어 인터페이스<br>GPIO 컨트롤러"]
    LED["LED 하드웨어"]
   
    %% 연결 흐름
    Browser -- "HTTP 요청" --> Apache
    Apache -- "프론트엔드 프로그램 전송" --> Browser
    Browser --> Frontend
    Frontend -- "WebSocket 연결" --> SocketIO
    Frontend -- "LED 제어 요청" --> SocketIO
    SocketIO -- "LED 상태 전송" --> Frontend
    SocketIO -- "GPIO 제어 명령" --> Hardware
    Hardware -- "LED 신호 전송" --> LED
    LED -- "상태 피드백" --> Hardware
    %% 서브그래프
    subgraph client[" "]
        Browser
        Frontend
    end

    subgraph server[" "]
        Apache
        SocketIO
        Hardware
    end
   
    %% 스타일 정의
    classDef client fill:#FFE0E0,stroke:#FF0000,stroke-width:3px
    classDef server fill:#E0E0FF,stroke:#0000FF,stroke-width:3px
    classDef led fill:#E0FFE0,stroke:#00AA00,stroke-width:3px
    %% 스타일 적용
    class client client
    class server server
    class LED led
  • 구성 이유
    • 정적 콘텐츠와 동적 통신 분리
      • Apache는 HTML, CSS, JS 같은 정적인 파일만 제공하므로 요청 처리 속도가 빠르고 안정적임.
      • 실시간 통신은 별도의 Socket.IO 서버가 담당하여 처리 부담을 분산함.
    • 역할별 모듈화
      • 웹 서버와 실시간 서버를 분리하면 유지보수와 확장에 유리함.
      • 각 모듈의 기능이 명확하게 분리되어 구조가 단순하고 이해하기 쉬움.
    • 실시간 제어에 적합한 구조
      • LED 같은 물리적 장치는 빠른 반응이 필요하므로 WebSocket 기반 통신이 적합함.
      • Socket.IO는 실시간 이벤트 기반 구조로 지연 없이 상태 전송이 가능함.
    • 개발 및 테스트 용이
      • 프론트엔드는 Apache로 언제든 새로고침 테스트 가능하고,
      • 서버 로직은 Python 단에서 독립적으로 개발 및 디버깅 가능함.
 
  • 라즈베리파이os에서 apache2 서버 설치
    • apt 패키지 도구로 apache2 서버 설치
    • /var/www/html 소유권 변경

sudo apt update  
#아파치 설치
sudo apt install apache2 -y
#아파치 상태 확인
sudo systemctl status apache2
#아파치 리부팅시 자동시작
sudo systemctl enable apache2
#아파치 root 폴더 권한 변경
sudo chown -R pi:pi /var/www/html


2. 프론트엔드 및 서버프로그램 개발
  • apaceh2 home 폴더에 프론트프로그램 개발
    • /var/www/html 폴더안에 index.html 파일 생성

<!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>
  • 우분투 환경에서 가상 GPIO 사용
    • 라즈베리파이 환경이 아닌 우분투 같은 테스트 환경에서 사용
    • GPIO 코드 사용시 에러 대체함
    • mockgpio.py 파일 생성

# 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서버 프로그램 개발
    • pip install flask flask-socketio eventlet 설치
    • home/iot/iot_socket.py 파일 생성

pi@pi20221234:~ $ iot_
(iot) pi@pi20221234:~/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

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