반응형

USB Serial Port를 사용해 보자.

 

 

AX781x0_MCS78x0_Win11_64bit_Driver_v3.22.2.0.zip
1.08MB

Windows 11(x64)

 

포트에 아무것도 연결하지 않은 상태에서 간단히 테스트할 수 있는 코드를 작성해 보자.

import serial
import time
import sys

serials = []
ports = ['COM2', 'COM7', 'COM8', 'COM9', 'COM10', 'COM11', 'COM12', 'COM13']
baudrate = 9600
databit = serial.EIGHTBITS
stopbit = serial.STOPBITS_ONE
parity = serial.PARITY_NONE

try:
    ser1 = serial.Serial(ports[0], baudrate, databit, parity, stopbit, timeout=1)
    ser2 = serial.Serial(ports[1], baudrate, databit, parity, stopbit, timeout=1)
    ser3 = serial.Serial(ports[2], baudrate, databit, parity, stopbit, timeout=1)
    ser4 = serial.Serial(ports[3], baudrate, databit, parity, stopbit, timeout=1)
    ser5 = serial.Serial(ports[4], baudrate, databit, parity, stopbit, timeout=1)
    ser6 = serial.Serial(ports[5], baudrate, databit, parity, stopbit, timeout=1)
    ser7 = serial.Serial(ports[6], baudrate, databit, parity, stopbit, timeout=1)
    ser8 = serial.Serial(ports[7], baudrate, databit, parity, stopbit, timeout=1)

    time.sleep(2)  # 장치 초기화 대기

    # 각 포트 연결 상태 확인
    for ser in [ser1, ser2, ser3, ser4, ser5, ser6, ser7, ser8]:
        if ser.is_open:
            print(f"{ser.port} 포트에 성공적으로 연결되었습니다.")
        else:
            print(f"{ser.port} 포트에 연결할 수 없습니다.")
            sys.exit(1)

    # 테스트 신호 5회 반복 전송
    for _ in range(5):
        for ser in [ser1, ser2, ser3, ser4, ser5, ser6, ser7, ser8]:
            test_signal = b"Test Signal\r\n"
            ser.write(test_signal)
            #print(f"{ser.port} 포트에 데이터 전송 완료: {test_signal}")
            time.sleep(0.2)

    """
    # 수신 데이터 확인 (응답이 있을 경우 출력)
    time.sleep(0.5)
    for ser in [ser1, ser2, ser3, ser4, ser5, ser6, ser7, ser8]:
        if ser.in_waiting > 0:
            response = ser.readline()
            print(f"{ser.port} 포트에서 수신된 데이터: {response}")
    """

    # 연결 종료
    for ser in [ser1, ser2, ser3, ser4, ser5, ser6, ser7, ser8]:
        ser.close()
        print(f"{ser.port} 포트를 안전하게 닫았습니다.")

except serial.SerialException as e:
    print(f"시리얼 포트 오류: {e}")
    
except Exception as e:
    print(f"오류 발생: {e}")

 

Tx LED가 순서대로 깜빡거린다.

 

 

이번엔 FIS 센서를 연결하고 데이터를 수신해 보자.

 

2026.05.15 - [Raspberry Pi & Arduino] - [Arduino] FIS Low concentration solvent gas sensor module

 

FIS 센서는 TTL 시그널을 사용하므로 RS232 - TTL 컨버터가 필요하다.

 

RS232 - TTL Converter

 

FIS Sensor RS232 - TTL Converter Power
1 VDD VCC VCC(5V)
2 VSS(GND) GND GND
6 SERIAL TXD  
7 RST   VCC(5V)

 

※ 주의

FIS Sensor의 6번(SERIAL) 핀은 Converter의 TXD에 연결해야 한다. RXD에 연결하면 안 된다.

센서에서 데이터가 계속 생성되도록 7번(RST) 핀은 VCC에 연결한다.

 

import serial
import time
import sys

try:
    serialPort = serial.Serial('COM2', 9600, 8, 'N', 1, timeout=1)
    # 시리얼 통신 설정. COM3 포트, 9600 보드레이트, 8 데이터 비트, 패리티 없음, 1 스톱 비트, 타임아웃 1초.
    time.sleep(1)  # 시리얼 연결이 초기화될 때까지 대기

except Exception as e:
    print("Serial error: ", e)
    sys.exit(0)

try:
    while (serialPort.readable()): # 시리얼 포트가 읽을 수 있는 상태인지 확인.
        if (serialPort.in_waiting > 0): # 시리얼 버퍼에 대기 중인 데이터가 있는지 확인.
            print(serialPort.readline().decode("utf-8", errors="ignore"), end="")
            # readline() 메서드를 사용하여 시리얼 포트에서 한 줄씩 데이터를 읽고 UTF-8로 디코딩하여 출력.
            # 오류가 발생할 경우 무시하도록 설정.
        else:
            print("No data waiting in the serial buffer.")

        time.sleep(0.2)

except KeyboardInterrupt:
    print("\n[알림] Ctrl+C 입력 감지. 프로그램을 종료합니다.")

finally:
    if 'serialPort' in locals() and serialPort.is_open:
        # locals() 함수를 사용하여 serialPort 변수가 정의되어 있는지 확인하고, 시리얼 포트가 열려 있는지 확인.
        serialPort.close()
        print("시리얼 포트가 안전하게 닫혔습니다.")

 

H0196에 알코올을 감지했다.

 

 

※ 참고

드라이버 다운로드 및 설치방법

 

반응형
Posted by J-sean
:
반응형

사용 중인 시리얼 포트를 확인해 보자.

 

장치관리자에서 확인해 보면 3개의 포트가 사용 중이다.

 

콘솔창에서 mode 명령어로 간단히 확인할 수 있다.

 

 

C++, CreateFile 예제

#include <windows.h>
#include <iostream>
#include <string>

int main() {
	std::cout << "Available COM Ports: \n";

	for (int i = 1; i < 256; ++i) {
		std::string portName = "\\\\.\\COM" + std::to_string(i);
		// \\.\com (코드 작성 시 이스케이프 문자를 적용한 \\\\.\\com)는 Windows 운영체제에서 직렬 포트(Serial Port)나 병렬 포트(LPT) 같은 하드웨어 장치에 직접 접근하기 위한 경로 형식이다.
		// 이 경로는 일반적으로 COM 포트에 접근할 때 사용된다. 예를 들어, COM1 포트에 접근하려면 "\\\\.\\COM1"과 같은 형식으로 경로를 지정한다.
		// 이 방식은 Windows에서 하드웨어 장치와 통신하기 위해 사용되는 표준적인 방법 중 하나이다.
		// 포트 번호가 10 이하인 경우에는 "COM1", "COM2"와 같이 간단히 사용할 수 있지만, 포트 번호가 10 이상인 경우에는 "\\\\.\\COM10"과 같이 전체 경로를 사용해야 한다.

		HANDLE hComm = CreateFileA(
			portName.c_str(), // 만들거나 열 파일 또는 디바이스의 이름.
			GENERIC_READ | GENERIC_WRITE, //파일 또는 디바이스에 대한 요청된 액세스이며 읽기, 쓰기, 둘 다 또는 0으로 요약할 수 있다.
			NULL, // 파일 또는 디바이스의 요청된 공유 모드.
			NULL, // 보안 속성에 대한 포인터.
			OPEN_EXISTING, // 파일이 존재하는 경우에만 열고, 그렇지 않으면 실패.
			NULL, // 파일 또는 디바이스에 대한 플래그 및 속성.
			NULL); // 템플릿 파일 핸들 또는 디바이스 핸들로 사용할 수 있는 유효한 핸들. 이 매개변수는 CreateFile이 새 파일을 만들 때만 사용. 이 매개변수는 일반적으로 NULL로 설정.

		if (hComm != INVALID_HANDLE_VALUE) {
			std::cout << "-> COM" << i << " is available.\n";
			CloseHandle(hComm);
		}
	}

	return 0;
}

 

 

C, QueryDosDevice 예제

#include <windows.h>
#include <stdio.h>

int main() {
	char deviceName[256];
	char comPort[16];
	char openPortName[32];

	printf("--- 사용 가능한 COM 포트 목록 ---\n");

	// COM1부터 COM256까지 가능한 포트 번호를 순회하며 확인
	for (int i = 1; i < 256; i++) {
		sprintf_s(comPort, sizeof(comPort), "COM%d", i);

		// QueryDosDevice를 이용해 해당 포트가 존재하는지 확인
		DWORD result = QueryDosDeviceA(comPort, deviceName, sizeof(deviceName));
		// CreateFile 함수로 포트를 열 때는 \\\\.\\COM10 형식을 써야 하지만, QueryDosDevice로
		// 시스템에 등록된 장치 이름을 조회할 때는 접두사(\\.\)를 붙이면 안된다.
		// 이 함수는 순수한 커널 장치 이름(예: COM1, COM10)만 인자로 받도록 설계되어 있다.

		if (result != 0) { // result는 deviceName에 복사된 문자열의 길이.
			// 실제 포트를 열거나 사용할 때는 "\\\\.\\" 접두사를 붙여서 출력 및 활용한다.
			sprintf_s(openPortName, sizeof(openPortName), "\\\\.\\%s", comPort);
			printf("%s 연결됨 (장치명: %s)\n", openPortName, deviceName);
		}
	}

	return 0;
}

 

 

어떤 프로그램이 시리얼 포트를 사용 중인지 조사할 때 File Handle의 이름으로 위 그림의 해당 장치명을 지정해야 찾을 수 있다.

Process Explorer - Find - Find Handle or DLL... 클릭 - Handle or DLL substring에 장치명을 입력하고 Search 클릭

시리얼 포트가 사용중이라면 사용하고 있는 프로세스가 표시된다. python.exe가 사용 중이다.

 

장치관리자에서는 포트 - 속성 - 자세히 - 서비스 - 값에 표시된 이름을 이용하자.

 

 

C# 예제

using System;
using System.IO.Ports;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // PC에서 사용 가능한 모든 시리얼 포트 배열 가져오기
            string[] ports = SerialPort.GetPortNames();

            Console.WriteLine("사용 가능한 시리얼 포트:");
            foreach (string port in ports)
            {
                Console.WriteLine(port);
            }

            // GetPortNames()는 PC에 잡혀있는 모든 포트를 반환하므로, 특정 포트가 현재 다른 프로그램에서
            // 사용 중이거나 연결 가능한 상태인지 확인하려면 직접 Open()을 시도해 봐야 한다.
            Console.WriteLine(Environment.NewLine + "연결 가능한 시리얼 포트:");
            foreach (string port in ports)
            {
                using (SerialPort serialPort = new SerialPort(port))
                {
                    try
                    {
                        serialPort.Open();
                        Console.WriteLine($"{port} : 연결 가능 (사용 가능)");
                        serialPort.Close();
                    }
                    catch (UnauthorizedAccessException)
                    {
                        Console.WriteLine($"{port} : 접근 거부 (다른 프로그램에서 사용 중)");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"{port} : 오류 발생 - {ex.Message}");
                    }
                }
            }
        }
    }
}

 

 

다른 프로그램에서 사용중인 경우

 

Python, pyserial 예제

import serial.tools.list_ports

# 사용 가능한 포트 리스트 가져오기
ports = serial.tools.list_ports.comports()

print("연결된 시리얼 포트 목록:")
print("-" * 30)
if ports:
    for port in ports:
        print(f"포트 이름: {port.device}")
        print(f"설명: {port.description}")
        print(f"하드웨어 ID: {port.hwid}")
        print("-" * 30)
else:
    print("사용 가능한 시리얼 포트가 없습니다.")

"""
# 각 포트에 대해 연결 가능 여부 확인
import serial

try:
    for port in ports:
        ser = serial.Serial(port.device)
        if ser.is_open:
            print(f"{port.device} : 연결 가능 (사용 가능)")
            ser.close()
        else:
            print(f"{port.device} : 연결 불가능 (사용 중)")

except serial.SerialException as e:
    print(f"시리얼 포트 확인 중 오류 발생: {e}")
"""

 

 

반응형
Posted by J-sean
: