LH-04

[LH-04] 윈도우 환경에서 MODBUS 릴레이지 제어

127.0.0.1을 이용해서 로컬에서 index.html이나 index.php 파일을 작동시키기 위해서는 웹 서버를 설정해야 합니다. Apache 설치 및 설정 (Windows 기준) Flask라는 Python […]

127.0.0.1을 이용해서 로컬에서 index.html이나 index.php 파일을 작동시키기 위해서는 웹 서버를 설정해야 합니다.

Apache 설치 및 설정 (Windows 기준)

  1. XAMPP와 같은 웹 서버 패키지를 다운로드하고 설치합니다.
  2. XAMPP Control Panel을 실행하고 Apache 서버를 시작합니다.
  3. htdocs 폴더(기본 경로는 C:\xampp\htdocs)에 index.php 파일을 생성합니다.
  4. 브라우저에서 http://127.0.0.1/index.php를 입력하여 PHP 파일을 실행합니다.

Flask라는 Python 웹 프레임워크를 사용하여 웹 브라우저에서 LH04 릴레이 모듈을 제어할 수 있는 웹 애플리케이션을 만들 수 있습니다. Flask를 사용하면 간단한 웹 서버를 만들고, HTML을 통해 사용자 인터페이스를 제공할 수 있습니다.

1. 필요한 라이브러리 설치

먼저 Flask와 minimalmodbus를 설치합니다.

pip install flask
pip install minimalmodbus
pip install pyserial

2. Flask 애플리케이션 코드 작성

다음 코드는 Flask를 사용하여 웹 기반 인터페이스를 제공하고, LH04 릴레이 모듈을 제어하는 예제입니다.

from flask import Flask, render_template, request, redirect, url_for
import minimalmodbus
import serial
import time

app = Flask(__name__)

# 포트와 보드레이트 설정
port = 'COM4' # 사용 중인 포트 이름으로 변경하세요
baudrate = 9600

# MODBUS 통신 설정
instrument = minimalmodbus.Instrument(port, 1) # 포트와 모듈 주소 설정 (기본 주소 1)
instrument.serial.baudrate = baudrate
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # 통신 타임아웃

def control_relay(relay_number, state):
"""
릴레이를 제어하는 함수
:param relay_number: 릴레이 번호 (1, 2, 3, 4)
:param state: 상태 (1: ON, 0: OFF)
"""
try:
if relay_number not in [1, 2, 3, 4]:
raise ValueError("Relay number must be 1, 2, 3, or 4.")
if state not in [0, 1]:
raise ValueError("State must be 0 (OFF) or 1 (ON).")

address = relay_number - 1 # 릴레이 번호에 따른 주소 설정
function_code = 5 # 단일 코일 제어 (MODBUS 기능 코드 5)
data = True if state == 1 else False # 릴레이 상태 설정

# 릴레이 제어 명령어 전송
instrument.write_bit(address, data, function_code)
time.sleep(0.1) # 명령어 전송 후 잠시 대기
except Exception as e:
print(f"릴레이 제어 오류: {e}")

@app.route('/')
def index():
return render_template('index.html')

@app.route('/relay', methods=['POST'])
def relay():
relay_number = int(request.form['relay_number'])
action = request.form['action']

if action == 'on':
control_relay(relay_number, 1)
elif action == 'off':
control_relay(relay_number, 0)

return redirect(url_for('index'))

if __name__ == '__main__':
app.run(debug=True)

3. HTML 템플릿 작성

Flask는 기본적으로 templates 폴더에 HTML 파일을 저장하여 렌더링합니다. httpd 안에 index.html 파일을 작성합니다.

templates/index.html

<!DOCTYPE html>
<html>
<head>
<title>릴레이 제어</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 50px;
}
.relay {
margin: 20px 0;
}
.relay label {
margin-right: 20px;
}
</style>
</head>
<body>
<h1>릴레이 제어</h1>
<div class="relay">
<form method="post" action="/relay">
<input type="hidden" name="relay_number" value="1">
<label>1번 스위치:</label>
<button type="submit" name="action" value="on">ON</button>
<button type="submit" name="action" value="off">OFF</button>
</form>
</div>
<div class="relay">
<form method="post" action="/relay">
<input type="hidden" name="relay_number" value="2">
<label>2번 스위치:</label>
<button type="submit" name="action" value="on">ON</button>
<button type="submit" name="action" value="off">OFF</button>
</form>
</div>
<div class="relay">
<form method="post" action="/relay">
<input type="hidden" name="relay_number" value="3">
<label>3번 스위치:</label>
<button type="submit" name="action" value="on">ON</button>
<button type="submit" name="action" value="off">OFF</button>
</form>
</div>
<div class="relay">
<form method="post" action="/relay">
<input type="hidden" name="relay_number" value="4">
<label>4번 스위치:</label>
<button type="submit" name="action" value="on">ON</button>
<button type="submit" name="action" value="off">OFF</button>
</form>
</div>
</body>
</html>

프로그램 실행

  1. Flask 애플리케이션 파일 저장: 위의 Python 코드를 app.py 파일로 저장합니다.
  2. HTML 템플릿 저장: 위의 HTML 코드를 c:/xampp/htdocs/relay/index.html 파일로 저장합니다.

*버튼을 원형으로 만듬

<!DOCTYPE html>
<html>
<head>
<title>LH-04 Relay Web Control</title>
<style>
.relay-container {
background-color: #e0e0e0; /* 회색 배경색 */
padding: 20px;
width: 200px;
margin: 0 auto;
text-align: center;
}
.relay-button {
display: inline-block;
margin: 10px;
padding: 20px; /* 버튼 크기를 크게 조정 */
background-color: #f0f0f0;
border: none;
border-radius: 50%; /* 원형 버튼 */
cursor: pointer;
font-size: 18px; /* 폰트 크기 */
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); /* 입체 효과 */
transition: all 0.3s ease; /* 애니메이션 효과 */
}
.relay-button.on {
background-color: #4CAF50; /* 파란색 배경색 */
color: white;
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5); /* 입체 효과 */
}
.relay-button:active {
box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.2); /* 클릭 시 눌리는 효과 */
}
</style>
</head>
<body>
<h1>LH-04 Relay Web Control</h1>
<div class="relay-container">
<button class="relay-button" id="relay1" onclick="toggleRelay(1)">1번 스위치</button>
<button class="relay-button" id="relay2" onclick="toggleRelay(2)">2번 스위치</button>
<button class="relay-button" id="relay3" onclick="toggleRelay(3)">3번 스위치</button>
<button class="relay-button" id="relay4" onclick="toggleRelay(4)">4번 스위치</button>
</div>

<script>
function controlRelay(relay_number, state) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/cgi-bin/control_relay2.py", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
xhr.send("relay_number=" + relay_number + "&state=" + state);
}

function toggleRelay(relay_number) {
var button = document.getElementById('relay' + relay_number);
var isOn = button.classList.toggle('on');
controlRelay(relay_number, isOn ? 1 : 0);
}

// prevent form submission behavior
document.addEventListener('click', function(event) {
if (event.target.matches('.relay-button')) {
event.preventDefault();
}
}, false);
</script>
</body>
</html>

  1. XAMPP와 같은 웹 서버 패키지를 다운로드하고 설치합니다.
  2. 가상 웹서버를 시작하기 위해 XAMPP Control Panel을 실행하고 Apache 서버를 시작합니다.

C:\xampp>xampp-control

[몇 가지 문제점 해결1] — 폰트 이슈

만일 index.html의 폰트가 깨지는 현상이 발생한다면 Apache 설정 파일(httpd.conf)에서 인코딩 설정을 추가할 수 있습니다.

XAMPP Control Panel에서 Apache 서버를 재시작합니다.

httpd.conf 파일 열기

C:\xampp\apache\conf\httpd.conf 파일을 엽니다.

AddDefaultCharset UTF-8 추가

Apache 재시작

[몇 가지 문제점 해결2] — 포트 80이 이미 사용 중인 문제

1. 현재 포트 80을 사용하는 프로그램 확인 및 종료
  1. 명령 프롬프트 (CMD) 열기
    • 윈도우 키 + R을 눌러 cmd 입력 후 Enter를 누릅니다.
  2. 포트 80을 사용하는 프로세스 확인
    • 명령어 netstat -ano | findstr :80를 입력하여 어떤 프로세스가 포트 80을 사용하고 있는지 확인합니다.
  3. 해당 프로세스 종료
    • 사용 중인 PID를 찾았으면 taskkill /PID <PID 번호> /F 명령어로 해당 프로세스를 강제로 종료합니다.
2. Apache의 포트 변경
  1. XAMPP Control Panel 열기
    • Apache 설정(Config) 버튼을 클릭하고 httpd.conf 파일을 엽니다.
  2. 포트 변경
    • Listen 80Listen 8080 등 다른 포트로 변경합니다.
    • ServerName localhost:80ServerName localhost:8080으로 변경합니다.
  3. XAMPP Control Panel에서 Apache 재시작
    • Apache를 다시 시작하여 변경 사항을 적용합니다.

.htaccess 파일 사용

.htaccess 파일 확인 및 생성

  1. .htaccess 파일 열기 또는 생성
    • C:/xampp/htdocs/relay 디렉토리에 .htaccess 파일이 있는지 확인하고, 없다면 새로 생성합니다.
  2. URL 재작성 규칙 추가
    • 필요한 경우 URL 재작성 규칙을 추가합니다.
RewriteEngine On

# relay URL을 relay/index.html로 매핑
RewriteRule ^relay$ /relay/index.html [L]

# 필요에 따라 추가 규칙 작성
# 예: 특정 스위치 제어 URL 매핑
RewriteRule ^relay/switch1$ /relay/switch1.php [L]
RewriteRule ^relay/switch2$ /relay/switch2.php [L]
RewriteRule ^relay/switch3$ /relay/switch3.php [L]
RewriteRule ^relay/switch4$ /relay/switch4.php [L]

스크립트 및 핸들러 확인

PHP 핸들러 확인

  1. PHP 파일 생성
    • C:/xampp/htdocs/relay 디렉토리에 relay.php 파일을 생성하고 다음과 같은 내용이 포함되어 있는지 확인합니다
<?php
echo "Relay Control Page";
// 여기에서 각 스위치의 상태를 제어하는 로직을 추가합니다.
?>

Python 스크립트를 웹서버에서 실행

웹서버에서 Python 스크립트를 실행하기 위해 CGI(Common Gateway Interface)를 사용할 수 있습니다.

2.1 CGI 설정
  1. httpd.conf 파일 수정:
    • httpd.conf 파일을 열고 아래 설정을 추가합니다
ScriptAlias /cgi-bin/ "C:/xampp/cgi-bin/"
<Directory "C:/xampp/cgi-bin">
AllowOverride None
Options +ExecCGI
Require all granted
AddHandler cgi-script .py
</Directory>
  1. CGI 디렉토리 생성:
    • C:/xampp/cgi-bin/ 디렉토리를 생성하고, control_relay2.py 파일을 해당 디렉토리에 복사합니다.
#!C:\Users\baro1\myenv\Scripts\python.exe

#!/usr/bin/python
# -*- coding: utf-8 -*-

import cgi
import minimalmodbus
import serial
import time

print("Content-Type: text/html\n")

# 포트와 보드레이트 설정
port = 'COM4' # 사용 중인 포트 이름으로 변경하세요
baudrate = 9600

# MODBUS 통신 설정
instrument = minimalmodbus.Instrument(port, 1) # 포트와 모듈 주소 설정 (기본 주소 1)
instrument.serial.baudrate = baudrate
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # 통신 타임아웃

def control_relay(relay_number, state):
"""
릴레이를 제어하는 함수
:param relay_number: 릴레이 번호 (1, 2, 3, 4)
:param state: 상태 (1: ON, 0: OFF)
"""
try:
if relay_number not in [1, 2, 3, 4]:
raise ValueError("Relay number must be 1, 2, 3, or 4.")
if state not in [0, 1]:
raise ValueError("State must be 0 (OFF) or 1 (ON).")

address = relay_number - 1 # 릴레이 번호에 따른 주소 설정
function_code = 5 # 단일 코일 제어 (MODBUS 기능 코드 5)
data = True if state == 1 else False # 릴레이 상태 설정

# 릴레이 제어 명령어 전송
instrument.write_bit(address, data, function_code)
time.sleep(0.1) # 명령어 전송 후 잠시 대기
print(f"Relay {relay_number} {'ON' if state == 1 else 'OFF'}")
except Exception as e:
print(f"릴레이 제어 오류: {e}")

form = cgi.FieldStorage()
relay_number = int(form.getvalue("relay_number", 0))
state = int(form.getvalue("state", 0))

if relay_number and state is not None:
control_relay(relay_number, state)
else:
print("Invalid input")

URL 테스트

브라우저에서 http://127.0.0.1:8080/relay/relay.php를 열어 페이지가 제대로 표시되는지 확인

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다