카테고리 없음

AIOT - 미니프로젝트(1) , 가변저항 일지 페이지 만들기

저선장 2023. 10. 24. 17:47

오늘은 그동안 공부한 아두이노 시리얼통신, 파이썬 시리얼통신, mysql, nodejs 등을 활용하여 가변저항 일지 페이지를 만드는 미니 프로젝트를 진행해보았습니다.

 

프로젝트 순서는 다음과 같습니다.

 

1. 시리얼통신을 통해 명령어를 전달받으면 가변저항 값을 출력하는 스케치를 작성한다
2. pyserial을 이용해 명령어를 전달하고, 출력값을 전달받을 수 있는 파이썬 스크립트를 작성한다.
3. 앞의 2번 파이썬 스크립트에 출력값을 전달받으면 MySQL을 이용해 데이터를 저장하는 기능을 추가로 구현한다(MySQL 테이블은 이미 만들어져 있는 상태로)
4. nodejs를 이용한 서버를 만들고, MySQL에서 데이터를 조회(GET)하는 REST API를 만든다. 생성, 수정, 삭제는 고려하지 않아도 됨.
5. REST API를 완성했다면, 이를 통해 데이터를 보여주는 웹 페이지를 만든다.

 

순서대로 코드공유와 함께 설명하겠습니다.

 

1. 시리얼통신을 통해 명령어를 전달받으면 가변저항 값을 출력하는 스케치를 작성한다

// 시리얼 통신으로 가변저항 값 얻기 예제

#define VR_PIN A0 

void setup(){
  Serial.begin(9600);
}

void loop(){
    if(Serial.available() > 0){
        String strRead = Serial.readStringUntil('\n');
        if(strRead.indexOf("VR") != -1 || strRead.indexOf("vr") != -1){
            Serial.print("VR=");
            Serial.println(analogRead(VR_PIN));
        }
    }    
}

스케치는 매우 간단합니다.

핀과 baudrate설정을 해주고, 루프에 명령문이 VR혹은 vr일 때 시리얼 창에 VR=(가변저항값)을 출력한다라는 간단한 코드입니다.

 

2. pyserial을 이용해 명령어를 전달하고, 출력값을 전달받을 수 있는 파이썬 스크립트를 작성한다.

'''
여기에서 구현해야 하는 것 
1. pyserial 로 가변저항 값 읽어들이기
2. 읽어들인 값 MySQL 테이블에 저장하기
'''
import time
import datetime
import serial
import serial.tools.list_ports
import threading
import pymysql

isDone = False
isFirst = True

# 시리얼 통신으로 들어오는 데이터 읽기 (from 보드)
def serial_read_thread():
    global isDone # 내가 수행하고자 하는 작업이 끝났는지에 대한 플래그
    while True:
        read_data = my_serial.readline()
        if read_data :
            print(read_data.decode())
            now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            curs.execute(f"INSERT INTO sensor VALUES ('{now}', 'VR', {int(read_data[3:])})")
            conn.commit()
            isDone = True

def main():
    global isDone
    global isFirst # 처음 한번은 무조건 해야함에 대한 플래그
    try:
        while True:
            if isDone or isFirst :
                comm = input("write serial command : ")
                if comm != "vr" and comm != "VR" : 
                    print("잘못된 명령입니다")
                    raise KeyboardInterrupt
                my_serial.write( comm.encode() )
                isDone = False
                isFirst = False

    except KeyboardInterrupt:
        pass


if __name__ == "__main__" :
    ports = list(serial.tools.list_ports.comports()) # 컴포트 참조
    for p in ports: # 컴포트 순회
        if 'CH340' in p.description:
            print(f"{p} 포트에 연결하였습니다.")
            my_serial = serial.Serial(p.device, baudrate=9600, timeout=1.0)
            time.sleep(2.0)    

    # MySQL연결(비밀번호 확인)
    conn = pymysql.connect(
        host="127.0.0.1", 
        user="root", 
        password="1234", 
        db="pydb", 
        charset="utf8"
    ) # 연결(스트림)
    curs = conn.cursor() # 스트림에서 작업을 수행하는 주체

    # 스레드 생성하여 별도로 수행할 작업 정하기
    t1 = threading.Thread(target=serial_read_thread)
    t1.daemon = True # 메인 스레드 종료 시 함께 종료되기
    t1.start()
    
    main()

    conn.close() 
    my_serial.close()

3. 앞의 2번 파이썬 스크립트에 출력값을 전달받으면 MySQL을 이용해 데이터를 저장하는 기능을 추가로 구현한다(MySQL 테이블은 이미 만들어져 있는 상태로)

 

MySQL 데이터베이스에 데이터를 저장하는 기능은 위에 이미 구현을 했고 DB참고를 위해 사용한 sql문만 추가로 보여드리겠습니다.

CREATE DATABASE pydb;

CREATE TABLE IF NOT EXISTS sensor (
	sensor_time datetime,
    sensor_name varchar(255),
    sensor_value int
);

 

4. nodejs를 이용한 서버를 만들고, MySQL에서 데이터를 조회(GET)하는 REST API를 만든다. 생성, 수정, 삭제는 고려하지 않아도 됨.

 

<package.json>

{
  "name": "vr_rest_api",
  "version": "1.0.0",
  "description": "MySQL table reading",
  "main": "app.js",
  "scripts": {
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2",
    "mysql2": "^3.6.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

<config.json>

module.exports = {
    host     : "127.0.0.1",
    user     : 'root',
    password : '1234',
    database : 'pydb'
}

<app.js>

const express    = require('express');
const server = express();
const PORT = process.env.PORT || 3000

const mysql      = require('mysql2');
const config   = require('./config');
const connection = mysql.createConnection(config); // 연결 객체

server.get('/sensor', (req, res) => {
    // 크로스오리진 이슈를 발생시키지 않도록 헤더 내용 추가
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    connection.query('SELECT * from sensor', (error, rows) => {
    if (error) throw error;
    console.log('User info is: ', rows);
    res.send(rows);
  });
});

server.listen(PORT, () => {
  console.log('Express server listening on port ' + PORT);
});

<node_modules>

상위 폴더를 통합 터미널에서 열어준 뒤, npm install을 해주면 자동으로 생성됩니다.

 

5. REST API를 완성했다면, 이를 통해 데이터를 보여주는 웹 페이지를 만든다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가변저항값 요청해서 보기</title>
    <style>
        #container > * {
            text-align: center;
        }
        table{
            width: 80%;
            max-width: 800px;
            min-width: 450px;
        }
    </style>
</head>
<body>
    <h2>저선장의 가변저항값 일지</h2>
    <div id="container"></div>
    
    <script>
        const request = new XMLHttpRequest() 
        const container = document.getElementById("container")
        request.open("GET", "http://localhost:3000/sensor")
        request.addEventListener("load", function(){
            // request.response : 요청 객체가 받은 응답이 들어있는 속성 (문자열)
            const response = JSON.parse(request.response)
            console.log(response) // JSON 객체 형태 확인
            container.innerHTML = `<table border="1">
                <tr>
                    <th>일시</th>
                    <th>센서</th>
                    <th>수치</th>
                </tr>
                    ${response.map(function(item){
                        const t = new Date(item.sensor_time)
                        const sensor_time = `${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`
                        return `<tr>
                                <td>${sensor_time}</td>
                                <td>${item.sensor_name}</td>
                                <td>${item.sensor_value}</td>
                            </tr>`
                    }).join(" ")}    
                </table>
            `
        }) // .join은 쉼표를 공백으로 바꿔줌
        request.send()
    </script>
</body>
</html>

 

결과화면입니다.

nodejs 서버 실행
pyserial로 명령 전달