[아두이노]Serial 통신3 (Flask 활용)
이번에는 Flask를 활용해서 2편에서 만든코드를 콘솔창이 아닌 웹페이지를 통해 제어할 것이다.
우선 우리는 이러한 웹사이트를 통해서 제어할 예정이다.
귀찮으면 소스코드 다운받기
localhost:5000
우선 app.py를 통해서 Flask 서버를 실행실킬 것이고, tmeplates 폴더는 render_template를 사용하기 때문에 필요하다.
그리고 LED.ino 를 아두이노에 넣어줘야된다.
우선 모듈이 없으면 모듈을 설치해주자
pip install flask
pip install pyserial
app.py
from flask import Flask, render_template, request
import serial
app = Flask(__name__)
# input값을 길이 32로 만들어줌
# ex) 51001100 -> 51001100!!!!!!!!!!!!!!!!!!!!!!!! (! 는 아두이노에서 continue 처리)
def len32(tx):
return str(tx) + ''.join(list('!' for i in range(32-len(tx))))
@app.route('/')
def index():
return render_template('index.html')
@app.route('/LED_ctrl/', methods=['post'])
def LED_ctrl():
py_serial = serial.Serial(
port='COM4', #본인에게 맞는 포트 설정해주기
baudrate=9600,
)
command = request.form['command']
serial_flag = 0
rx_result = ''
while True:
if py_serial.readable():
rx = py_serial.readline()[:-1].decode()
print(rx)
rx_result = rx_result + rx + '\n'
# 명령어 전송
if serial_flag==0 :
tx = len32(command)
py_serial.write(tx.encode())
serial_flag = 1
if rx[0:9] == 'Complete!':
break
return render_template('index.html', result = rx_result)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000, debug=True)
설명
우선 def LED_ctrl 를 보면 우선 py_serial를 통해서 serial 통신을 할 준비를 해준 후
(이 코드를 밖에 빼면 좋았겠지만 이유는 잘 모르겟지만 액세스가 거부 오류가 떠서 안으로 넣어줬다)
command : html의 input박스에서 넣어준 값을 request.form['command']를 통해 할당시켜줬다.
serial_flag : 입력을 한번만 받기 위해서 만들어준 변수이다.
rx_result : 나중에 출력값을 webpage에 출력해주기 위해서 만들어준 문자열이다.
while True를 통해서 무한루프를 돌면서 출력값을 rx_result로 받아준 후
if문을 통해서 'Complete!'가 rx에 들어가게 되면 무한 루프를 종료 시켜줬다.

이 코드의 경우는 Flask를 처음 접하면 어려울 수 도 있는데
render_template 는 webpage에 html 파일을 출력시켜주는 함수이다. 그래서 index.html 를 출력시켜준다.
html 파일은 반드시 templates 폴더 안에 있어야한다.
그리고 콤마(,) 뒤에 result=rx_result는 왼쪽의 result는 html 내에서 사용하는 변수이고 그 값을 할당시켜주는 것 이다.
아래 index.html 코드를 보면 {{ result }} 라는 코드가 있는데 이 곳에 rx_result를 대입시켜주는 것 이다.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Serial_17eehyuk</title>
<script>
function num_only(this_plz){
this_plz.value = this_plz.value.replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');
}
</script>
</head>
<body>
<h2>Serial 통신3(17eehyuk)</h2>
<form method="post">
<input type="text" name="command" maxlength="32" oninput="num_only(this)" required>
<input type="submit" value="전송" formaction="/LED_ctrl/">
</form>
<pre>{{ result }}</pre> {# 출력을 그대로 받기 위해서 pre 태그로 감싸줌 #}
</body>
</html>
설명
num_only : 숫자만 입력받을 수 있는 함수이다.
'this_plz' 라고 한 이유는 'this'를 쓰면 좋았겠지만, this는 매개변수명으로 사용할 수 없으므로 'this_plz' 라고 작성했다.
그 외에는 form을 통해서 command를 전송시켜주는 간단한 코드이다.
LED.ino
void setup() {
pinMode(2, OUTPUT);pinMode(3, OUTPUT);pinMode(4, OUTPUT);pinMode(5, OUTPUT);
pinMode(6, OUTPUT);pinMode(7, OUTPUT);pinMode(8, OUTPUT);pinMode(9, OUTPUT);
Serial.begin(9600);
}
void loop() {
Serial.print("Serial Start!\n");
while(Serial.available()==0){
}
String rx = Serial.readString();
for(int i=0; i<8; i++){
if(rx[4*i]=='!'){continue;}
char led = rx[4*i]-'0'+2; //문자 -> 숫자 (+2는 내가 핀을 2번부터 시작하기 때문)
int opt = 10*(100*(rx[4*i+1]-'0')+10*(rx[4*i+2]-'0')+1*(rx[4*i+3]-'0')); //operating time
//시리얼 메세지
char msg[40];
sprintf(msg, "%d번 LED %d ms", led-2, opt); //메세지는 -2 해줘야 정확하게 출력
Serial.println(msg);
//LED 작동
digitalWrite(led, HIGH);
delay(opt);
digitalWrite(led, LOW);
};
Serial.print("Complete!\n");
}
아두이노는 2편과 동일하므로 2편코드를 참조바란다.(https://17eehyuk.tistory.com/24)
약간의 아쉬운점이 있다면 결과가 출력될 때 까지 페이지가 멈추는 점이고(동기식이기 때문)
비동기처리를 통해서 결과를 한줄씩 출력해주면 좋겠지만
나는 아직 ajax를 어떻게 다루는지 모르기 때문에 기회가 되면 4편에서 좀더 예쁘게 만들겠다.