반응형

시리얼 릴레이 사용법을 알아보자.

 

 

A frame instruction is generally composed of device address, function code, register address, register data, check code, and frame length is related to the function code.
Generally, the first byte of each frame data is the device address, which can be set to 1~255. The default is 255(0xFF), and the last two bytes are the CRC check code.

- Turn on relay_1
FF 05 00 00 FF 00 99 E4
3~4 바이트: 릴레이 번호
5~6 바이트: FF00 = 릴레이 켜기, 0000 = 릴레이 끄기
마지막 두 바이트: CRC16 (명령어가 바뀔때마다 다시 계산해야 한다)

- Turn on relay_2
FF 05 00 01 FF 00 C8 24

- Turn off relay_1
FF 05 00 00 00 00 D8 14

 

- Turn off relay_2
FF 05 00 01 00 00 89 D4

- Turn on all relays
FF 0F 00 00 00 08 01 FF 30 1D

- Turn off all relays
FF 0F 00 00 00 08 01 00 70 5D

- 1번 릴레이 2초 켰다 끄기
FF 10 00 03 00 02 04 00 04 00 14 C5 9F
3~4 바이트: 릴레이 번호, 0003=1번, 0008=2번, 000D=3번, 0012=4번, 0017=5번, 001C=6번
10~11 바이트: 딜레이 시간, 10~11바이트 값에 0.1초를 곱하는 숫자가 딜레이 시간, 0014*0.1 = 20*0.1 = 2초

- 3번 릴레이 5초 켰다 끄기
FF 10 00 0D 00 02 04 00 04 00 32 C5 C9

- 3초 후 릴레이 1번 켜기 (켜져 있는 상태에서 하면 꺼졌다가 3초 후 다시 켜진다)
FF 10 00 03 00 02 04 00 02 00 1E A5 99
3~4 바이트: 릴레이 번호, 0003=1번, 0008=2번, 000D=3번, 0012=4번, 0017=5번, 001C=6번
10~11 바이트: 딜레이 시간, 10~11바이트 값에 0.1초를 곱하는 숫자가 딜레이 시간, 001E*0.1 = 30*0.1 = 3초

- 5초 후 릴레이 4번 켜기 (켜져 있는 상태에서 하면 꺼졌다가 5초 후 다시 켜진다)
FF 10 00 12 00 02 04 00 02 00 32 64 84

- Read device address
00 03 00 00 00 01 85 DB
결과로 돌아오는 값에서 5번째 바이트가 주소 (ex: FF)

- Read baud rate
FF 03 03 E8 00 01 11 A4
결과로 돌아오는 값에서 5번째 바이트가 baud rate
0x02=4,800 0x03=9,600 0x04=19,200

 

아래는 JK-SR-2 시리얼 릴레이 사용법이다.

컴퓨터에 연결할 때 JK-SR-2의 시리얼 포트(RS-232) 9핀을 분리해서 USB to UART 컨버터에 연결해 사용하지 말고 USB to RS-232/DB-9 케이블(컨버터)을 사용하자. RS-232와 UART(TTL)는 신호 레벨이 다르고(12V, 5V) 논리도(정논리, 부논리) 다르기 때문에 UART의 RX, TX, GND 핀을 연결해도 이상하게 작동한다.

실제 연결해서 확인해 보면 논리가 다르기 때문인지, 연결하자마자 아무 메세지를 보내지 않아도 끊임없이 FF값이 수신된다.(위 파란색 제품도 마찬가지일 듯..)

 

 

 

아래는 Ethernet (Wi‑Fi) Relay 설명이다.

 

I1~I8과 GND를 단락시켜서 릴레이 조작이 가능하다. (스위치를 만들 수 있을것이다)

 

relay_sdk.z01
19.53MB
relay_sdk.z02
19.53MB
relay_sdk.z03
19.53MB
relay_sdk.zip
11.20MB

 

Ethernet은 컴퓨터와 랜케이블로 연결하고 192.168.1.100에 접속하면 간단히 되는데 Wi-Fi는 어떻게 해야 할지 모르겠다. 잘 안 된다.

 

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

컴퓨터와 다른 기기 사이에 시리얼 통신을 해 보자.

 

hterm-windows.zip
2.14MB

 

 

실행화면

 

Port, Baud를 맞추고 Connect 버튼을 누르면 연결된다. Input control의 Type을 HEX로 바꾸고 메세지를 보낸다.

 

※ 참고

HTerm

Arduino Hex 명령

CRC16 계산기

CRC 계산기 (시리얼 통신용 CRC는 'CRC-16/MODBUS'를 확인하면 된다)

 

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

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
:
반응형

USB to UART Converter를 사용해 시리얼 통신을 해 보자.

 

 

링크에서 드라이버를 다운로드받거나 아래 파일을 다운로드받고 설치한다.

CP210x_Windows_Drivers.zip
6.84MB

 

 

드라이버 설치 후 USB to UART Converter를 컴퓨터에 연결하면 정상적으로 포트에 잡힌다.

 

FIS Sensor를 연결하고 테스트해 보자.

FIS Sensor USB to UART Converter
1 VDD 5V
2 VSS(GND) GND
6 SERIAL RXD
7 RST 5V

 

센서 자체에서 시리얼 통신으로 데이터를 보내오기 때문에 아두이노 같은 컨트롤러가 필요 없다.

아래 파이썬 코드를 입력하고 실행한다.

import serial
import time
import sys

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

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

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

serialPort.close()

 

 

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

C#과 아두이노 시리얼 통신을 해 보자.

 

 

 

적외선 온도센서 GY-906을 아두이노에 연결한다.

 

VIN - 5V

GND - GND

SCL - A5

SDA - A4

 

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  mlx.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("Ambient Temp: ");
  Serial.println(mlx.readAmbientTempC());
  Serial.print("Object Temp: ");
  Serial.println(mlx.readObjectTempC());
  delay(1000);
}

아두이노에 위 코드를 업로드한다.

 

using System;
using System.Threading;
using System.IO.Ports;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            SerialPort serialPort = new SerialPort();

            try
            {
                serialPort.PortName = "COM3"; // 시리얼 포트 이름을 "COM3"으로 설정한다. 실제로 사용하려는 시리얼 포트 이름이다.
                serialPort.BaudRate = 9600; // 시리얼 통신의 전송 속도를 9600 보드로 설정한다. 이는 일반적으로 사용되는 보드레이트 중 하나이다.
                serialPort.DataBits = 8; // 8비트의 데이터 비트를 사용하여 데이터를 전송한다. 이는 일반적으로 가장 많이 사용되는 데이터 비트 설정이다.
                serialPort.StopBits = StopBits.One; // 1비트의 정지 비트를 사용하여 데이터 전송이 끝났음을 나타낸다.
                serialPort.Parity = Parity.None; // 패리티 비트를 사용하지 않는다. 데이터 전송에서 오류 검출을 위해 패리티 비트를 사용할 수 있지만, 여기서는 사용하지 않는다.

                Thread.Sleep(2000); // 시리얼 포트 설정이 완료된 후 잠시 대기하여 안정적으로 연결될 수 있도록 한다.
                serialPort.Open(); // 시리얼 포트를 연다. 이 단계에서 설정된 포트 이름과 통신 속도 등이 적용되어 시리얼 통신이 시작된다.
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }

            while (serialPort.IsOpen)
            {
                try
                {
                    string data = serialPort.ReadLine(); // 시리얼 포트에서 한 줄의 데이터를 읽어온다. 데이터가 줄 바꿈 문자로 끝날 때까지 읽는다.
                    Console.WriteLine(data);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error: " + ex.Message);
                }

                Thread.Sleep(100);
            }
        }
    }
}

C# 코드를 입력하고 실행한다.

 

 

※ 참고

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

 

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

Windows API(CreateFile)을 이용해 간단히 아두이노와 시리얼 통신을 할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
char state;
 
void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("Arduino ready.");
}
 
void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available())
  {
    state = Serial.read();
    while (Serial.available())
    {
      Serial.read();  // 첫 번째 문자만 입력받고 나머지는 버린다.
    }
    
    if (state == '0')
    {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("LED OFF");
    } else
    {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("LED ON");
    }
  }
 
  delay(100);
}
 

위 소스를 컴파일 하고 아두이노에 업로드 한다. 시리얼 모니터를 통해서도 Builtin LED를 제어할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef SERIALCLASS_H_INCLUDED
#define SERIALCLASS_H_INCLUDED
 
#define ARDUINO_WAIT_TIME 2000
 
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
 
class Serial
{
private:
    //Serial comm handler
    HANDLE hSerial;
    //Connection status
    bool connected;
    //Get various information about the connection
    COMSTAT status;
    //Keep track of last error
    DWORD errors;
 
public:
    //Initialize Serial communication with the given COM port
    Serial(const char* portName);
    //Close the connection
    ~Serial();
    //Read data in a buffer, if nbChar is greater than the
    //maximum number of bytes available, it will return only the
    //bytes available. The function return -1 when nothing could
    //be read, the number of bytes actually read.
    int ReadData(char* buffer, unsigned int nbChar);
    //Writes data from a buffer through the Serial connection
    //return true on success.
    bool WriteData(const char* buffer, unsigned int nbChar);
    //Check if we are actually connected
    bool IsConnected();
 
 
};
 
#endif // SERIALCLASS_H_INCLUDED
 

Serial class header.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "SerialHeader.h"
 
Serial::Serial(const char* portName)
{
    //We're not yet connected
    this->connected = false;
 
    //Try to connect to the given port throuh CreateFile
    //CreateFile may need to be replaced with CreateFileA or...
    //Project - XXX Properties - Configuration Properties - Advanced - Character Set - Use Multi-Byte Character Set
    this->hSerial = CreateFileA(portName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
 
    //Check if the connection was successfull
    if (this->hSerial == INVALID_HANDLE_VALUE)
    {
        //If not success full display an Error
        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
 
            //Print Error if neccessary
            printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);
 
        }
        else
        {
            printf("ERROR!!!");
        }
    }
    else
    {
        //If connected we try to set the comm parameters
        DCB dcbSerialParams = { 0 };
 
        //Try to get the current
        if (!GetCommState(this->hSerial, &dcbSerialParams))
        {
            //If impossible, show an error
            printf("failed to get current serial parameters!");
        }
        else
        {
            //Define serial connection parameters for the arduino board
            dcbSerialParams.BaudRate = CBR_9600;
            dcbSerialParams.ByteSize = 8;
            dcbSerialParams.StopBits = ONESTOPBIT;
            dcbSerialParams.Parity = NOPARITY;
            //Setting the DTR to Control_Enable ensures that the Arduino is properly
            //reset upon establishing a connection
            dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
 
            //Set the parameters and check for their proper application
            if (!SetCommState(hSerial, &dcbSerialParams))
            {
                printf("ALERT: Could not set Serial Port parameters");
            }
            else
            {
                //If everything went fine we're connected
                this->connected = true;
                //Flush any remaining characters in the buffers 
                PurgeComm(this->hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);
                //We wait 2s as the arduino board will be reseting
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
 
}
 
Serial::~Serial()
{
    //Check if we are connected before trying to disconnect
    if (this->connected)
    {
        //We're no longer connected
        this->connected = false;
        //Close the serial handler
        CloseHandle(this->hSerial);
    }
}
 
int Serial::ReadData(char* buffer, unsigned int nbChar)
{
    //Number of bytes we'll have read
    DWORD bytesRead;
    //Number of bytes we'll really ask to read
    unsigned int toRead;
 
    //Use the ClearCommError function to get status info on the Serial port
    ClearCommError(this->hSerial, &this->errors, &this->status);
 
    //Check if there is something to read
    if (this->status.cbInQue > 0)
    {
        //If there is we check if there is enough data to read the required number
        //of characters, if not we'll read only the available characters to prevent
        //locking of the application.
        if (this->status.cbInQue > nbChar)
        {
            toRead = nbChar;
        }
        else
        {
            toRead = this->status.cbInQue;
        }
 
        //Try to read the require number of chars, and return the number of read bytes on success
        memset(buffer, 0, nbChar);
        if (ReadFile(this->hSerial, buffer, toRead, &bytesRead, NULL))
        {
            return bytesRead;
        }
 
    }
 
    //If nothing has been read, or that an error was detected return 0
    return 0;
 
}
 
 
bool Serial::WriteData(const char* buffer, unsigned int nbChar)
{
    DWORD bytesSend;
 
    //Try to write the buffer on the Serial port
    if (!WriteFile(this->hSerial, (void*)buffer, nbChar, &bytesSend, 0))
    {
        //In case it don't work get comm error and return false
        ClearCommError(this->hSerial, &this->errors, &this->status);
 
        return false;
    }
    else
        return true;
}
 
bool Serial::IsConnected()
{
    //Simply return the connection status
    return this->connected;
}
 

Serial class source.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include "SerialHeader.h"
 
using namespace std;
 
int main()
{
    Serial* ser = new Serial("\\\\.\\COM3");
    char message[255];    
 
    if (ser->IsConnected()) {
        cout << "Serial Communication Connected." << endl;
        // memset(message, 0, sizeof(message));
        // Serial::ReadData() 내부에서 memset이 실행된다.
        ser->ReadData(message, sizeof(message));
        cout << "Message from Arduino: " << message << endl;
    } else {
        cout << "Device can not be found or can not be configured." << endl;
 
        return 0;
    }
    
    while (true) {
        cout << "0: Off, 1 : On, q(Q) : Quit" << endl << "Choose : ";        
        cin >> message;
 
        if (!strcmp(message, "q"|| !strcmp(message, "Q")) {
            break;
        } else if (!strcmp(message, "0")) {            
            ser->WriteData("0"1);
            Sleep(200); // 아두이노와의 시리얼 통신을 위한 대기 시간.            
            ser->ReadData(message, sizeof(message));
            cout << "Message from Arduino: " << message << endl;
        } else {
            ser->WriteData("1"1);
            Sleep(200); // 아두이노와의 시리얼 통신을 위한 대기 시간.            
            ser->ReadData(message, sizeof(message));
            cout << "Message from Arduino: " << message << endl;
        }
    }
 
    return 0;
}
 

Windows에서 위 소스를 실행하면 연결된 아두이노의 Builtin LED를 제어할 수 있다.

 

※ 참고: Arduino and C++ (for Windows)

 

 

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

파이썬 모듈인 pySerial을 이용해 간단히 아두이노와 시리얼 통신을 할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
char state;
 
void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("Arduino ready.");
}
 
void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available())
  {
    state = Serial.read();
    while (Serial.available())
    {
      Serial.read();  // 첫 번째 문자만 입력받고 나머지는 버린다.
    }
    
    if (state == '0')
    {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("LED OFF");
    } else
    {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("LED ON");
    }
  }
 
  delay(100);
}
 
 

위 소스를 컴파일하고 아두이노에 업로드한다. 시리얼 모니터를 통해서도 Builtin LED를 제어할 수 있다.

 

 

먼저 pip install pyserial 명령으로 pyserial 라이브러리를 설치한다.

(serial이 아닌 pyserial이다, serial은 다른 라이브러리다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import serial
import time
import sys
 
try:
    ser = serial.Serial('COM5'9600, timeout = 1)
    time.sleep(1)
except:
    print("Device can not be found or can not be configured.")
    sys.exit(0)
 
if (ser.readable()):
    print(ser.readline().decode(), end =''# 아두이노 준비 상태 확인.
    # 아두이노에서 Serial.println("Arduino ready"); 명령으로 데이터를 보내기 때문에
    # Serial 통신으로 읽어온 데이터에는 줄바꿈 문자(\r\n)가 이미 포함되어 있다.
    # Serial.print("Arduino ready");으로 바꾸면 end = ''가 필요 없다. 
 
while (True):
    print("\n0: Off, 1: On, q(Q): Quit\nChoose: ", end = '')    
    state = input()
 
    if state == 'q' or state == 'Q':
        break
    elif state == '0':
        ser.write(b'0')
        print(ser.readline().decode(), end = '')
    else:
        ser.write(b'1')
        print(ser.readline().decode(), end = '')
 
    time.sleep(0.1)
 
ser.close()
 
 

Windows에서 위 소스를 실행하면 연결된 아두이노의 Builtin LED를 제어할 수 있다.

 

이 프로그램과 Arduino IDE의 시리얼 모니터를 동시에 실행할 수는 없다.

 

 

이번에는 적외선 온도 센서를 이용해 온도 측정 데이터를 받아 보자.

 

 

 

VIN - 5V

GND - GND

SCL - A5

SDA - A4

 

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  mlx.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("Ambient Temp: ");
  Serial.println(mlx.readAmbientTempC());
  Serial.print("Object Temp: ");
  Serial.println(mlx.readObjectTempC());
  delay(1000);
}

아두이노에 위 코드를 업로드한다.

 

import serial
import time
import sys

try:
    serialPort = serial.Serial('COM3', 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.1)

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

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

파이썬으로 위 코드를 입력하고 실행한다.

 

 

반응형
Posted by J-sean
: