https://m.blog.naver.com/ansdbtls4067/220886156177

병렬과 직렬의 차이점은 위와 같다. 직렬은 하나의 통신선만 사용하기에 저렴하다. USB, PC COM port등이 있다.

동기 시리얼 통신은 별도의 통신을 수행하는 동기화 Clock선이 존재한다. 따라서 오류가 적고 빠른 데이터 통신이 가능하지만 통신비용이 증가되고 전자회로가 다소 복잡해진다.
비동기 통신의 경우 통신선과 제어선만 구성되어 2진 데이터 신호가 통신 회선을 통해 이동하는 과정 중 원본 데이터에 대한 2진 데이터 신호가 사라지거나 동기 시리얼 통신에 비해 전송할 데이터의 처음과 끝에 각종 통신 제어관련 정보를 함께 실어 보내야 한다. 따라서 속도가 비교적 느리다.
Grok의 serial 통신 설명
시리얼 통신(Serial Communication)은 데이터를 한 번에 한 비트씩 순차적으로 전송하는 통신 방식입니다. 병렬 통신(Parallel Communication)이 데이터를 여러 비트를 동시에 전송하는 것과 달리, 시리얼 통신은 데이터를 일렬로 나열해 시간 순서대로 보내는 방식이라 전선 수가 적어도 되며, 장거리 통신이나 간단한 장치 간 연결에 유리합니다.
데이터 전송 방식:
동기 vs 비동기:
보드레이트(Baud Rate):
전송 방향:
from serial import Serial
from serial.tools.list_ports import comports
Serial: pyserial 라이브러리의 핵심 클래스. 시리얼 포트를 열고 데이터를 읽거나 쓰는 데 사용됩니다.comports: 사용 가능한 COM 포트 목록을 가져오는 유틸리티 함수. GUI에서 포트 선택 드롭다운을 채우는 데 활용됩니다.class SerialReaderGUI(QMainWindow):
def __init__(self):
super().__init__()
# ... (다른 GUI 초기화 코드 생략) ...
self.ser = None # 시리얼 객체 초기화
self.is_connected = False # 연결 상태 플래그
self.ser: 시리얼 통신 객체를 저장하는 변수로, 초기에는 None으로 설정됩니다. 이후 연결 시 Serial 객체로 대체됩니다.self.is_connected: 시리얼 포트 연결 여부를 추적하는 플래그.def update_ports(self):
self.port_combo.clear()
ports = [port.device for port in comports()] # 사용 가능한 포트 목록 가져오기
if ports:
self.port_combo.addItems(ports)
self.log_message(f"포트 목록 업데이트: {', '.join(ports)}")
else:
self.log_message("사용 가능한 COM 포트가 없습니다.")
comports(): 시스템에서 사용 가능한 모든 시리얼 포트를 반환합니다. 각 포트는 port.device 속성으로 이름(예: 'COM3', '/dev/ttyUSB0')을 제공합니다.QComboBox에 포트 목록을 채우고, 로그에 업데이트된 포트 정보를 기록합니다.def connect_serial(self):
try:
port = self.port_combo.currentText() # 선택된 포트 가져오기
self.ser = Serial(port=port, baudrate=9600, timeout=1) # 시리얼 객체 생성
self.is_connected = True
self.connect_button.setText('연결 해제')
self.status_label.setText(f'상태: {port}에 연결됨')
self.log_message(f'연결 성공: {port}')
self.timer.start() # 데이터 읽기 타이머 시작
except Exception as e:
self.log_message(f'연결 실패: {str(e)}')
Serial(port, baudrate, timeout): 시리얼 포트를 엽니다.port: 선택된 COM 포트 (예: 'COM3').baudrate=9600: 데이터 전송 속도. 수분 측정기가 9600bps로 설정되어 있다고 가정.timeout=1: 읽기 타임아웃을 1초로 설정. 데이터가 없으면 1초 후 읽기를 종료.self.ser에 시리얼 객체 저장.self.timer.start()로 주기적 데이터 읽기 시작.def disconnect_serial(self):
if self.ser and self.ser.is_open: # 시리얼 객체가 존재하고 열려 있는지 확인
self.timer.stop() # 데이터 읽기 타이머 중지
self.ser.close() # 시리얼 포트 닫기
self.is_connected = False
self.connect_button.setText('연결')
self.status_label.setText('상태: 연결 안됨')
self.log_message('연결 종료')
self.ser.is_open: 포트가 열려 있는지 확인.self.ser.close(): 열려 있는 시리얼 포트를 닫음.def read_data(self):
if not self.ser or not self.ser.is_open: # 시리얼 객체가 없거나 닫혀 있으면 종료
return
try:
if self.ser.in_waiting: # 수신 버퍼에 데이터가 있는지 확인
data = self.ser.readline().decode('ascii').strip() # 데이터 읽기
if data:
self.log_message(f'수신: {data}')
if 'Final Result' in data:
# "Final Result" 뒤의 숫자 추출
match = re.search(r'Final Result[.\s]+([\d.]+)', data)
if match:
result_value = match.group(1)
self.log_message(f'클립보드에 복사됨: {result_value}')
pyperclip.copy(result_value) # 클립보드에 복사
self.log_message(f'커서에 붙여넣기 시작: {result_value}')
pyautogui.write(result_value, interval=0.01) # 현재 커서에 입력
pyautogui.press('enter') # Enter 키 입력
self.log_message(f'붙여넣기 완료: {result_value}')
else:
self.log_message('숫자 추출 실패')
except Exception as e:
self.log_message(f'데이터 읽기 오류: {str(e)}')
self.ser.in_waiting: 수신 버퍼에 읽을 수 있는 바이트 수가 있는지 확인. 0이면 데이터가 없음.self.ser.readline(): 한 줄(개행 문자 \n까지)을 읽음. 타임아웃이 설정되어 있으므로 데이터가 없으면 1초 후 빈 값을 반환.decode('ascii'): 바이트 데이터를 ASCII 문자열로 변환. 수분 측정기가 다른 인코딩(예: UTF-8)을 사용한다면 수정 필요.strip(): 문자열 양쪽의 공백/개행 문자 제거.re.search)으로 숫자(소수점 포함)를 추출.pyperclip.copy)하고, 현재 커서 위치에 입력(pyautogui.write) 후 Enter 키 입력.def closeEvent(self, event):
self.disconnect_serial() # 창을 닫을 때 시리얼 연결 해제
event.accept()
closeEvent: PyQt5에서 창이 닫힐 때 호출되는 메서드.comports()로 사용 가능한 포트를 GUI에 표시.Serial 객체를 생성해 선택한 포트(baudrate=9600)에 연결.QTimer)으로 readline()을 호출해 데이터를 주기적으로 읽음.baudrate 값을 조정해야 합니다.re.search)을 수정해야 할 수 있습니다.self.timer.setInterval(50)은 50ms마다 읽기를 시도하는데, 데이터 전송 주기가 느리다면 간격을 늘릴 수 있습니다.