[아두이노, C#]Delegate, BeginInvoke 로 시리얼 통신 진행하기 | dasfef

2023.03.29


아두이노와 C# 의 시리얼 통신


1. 아두이노 통신 체크

int Count = 0;

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

}

void loop() {
  Serial.println(Count);
  if(Count++ > 9999) Count = 0;
  delay(500);
}

정상 연결 및 동작 시 0.5초마다 1씩 증가하며 시리얼 모니터에 출력된다
​아두이노에 이상 없음 체크


2. C# 윈도우 폼 디자인

C# 내 간단한 윈도우 폼 디자인 도구 : textBox Dock : Fill


3. 코드 구성

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;                          // 포트 설정을 위한 호출

namespace Serial_to_LCD
{
    public partial class Form1 : Form
    {
        SerialPort ComPort = new SerialPort();  // 전역변수 ComPort 설정
        public Form1()
        {
            InitializeComponent();
        }
    }
}

위와 같이 폼 로드 전에 구성을 해주고

​폼 로드와 클로징 시 필요한 코드를 구성한다

private void Form1_Load(object sender, EventArgs e)
        {
            ComPort.PortName = "COM3";          // 아두이노가 연결된 포트 번호
            ComPort.BaudRate = 9600;            // 일반적으로 9600
            ComPort.DataBits = 8;               // 통신의 비트 설정 : 일반적으로 8비트
            ComPort.Parity = Parity.None;       // 통신의 정확도를 위한 패리티 검사(1의 개수의 짝/홀 여부)
            ComPort.StopBits = StopBits.One;    // 통신 말미 1로 지정
            ComPort.Handshake = Handshake.None;
            ComPort.Open();
            ComPort.DiscardInBuffer();          // 수신 버퍼 데이터 삭제
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            ComPort.Close();                    // 시리얼 포트 닫음
            ComPort.Dispose();                  // 시리얼 포트 완전 해제
            ComPort = null;
        }

폼 로드시 구성되는 코드 중 databits, parity, stopbits, handshake 는 시리얼 포트 세팅 옵션이다

일반적으로 위와 같은 구성이 기본값으로 구성되며, 정확한 통신 세팅을 위해선 특정 값을 지정해주지만 간단한 구성에선 제외해도 구동이 가능하다

​하지만 그럼에도 여러 옵션이 있다는 것은 알아두면 나쁘지 않으니 아래에 정리해두자

databits : 8, 7, 6 parity : none, even, mark, odd, space handshake : none, Xon/Xoff, request to send, request to send Xon/Xoff


🌞 이벤트 핸들러 생성

public Form1()
        {
            InitializeComponent();
            ComPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
        }

        private void DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string rxd = ComPort.ReadTo("\n");
        }

Comport.ReadTo() 로 \n 까지 읽고 데이터를 Delegate 로 송신한다


🌞 delegate 구성

처음 보면 이해하기 어렵다 😰 delegate 는 대리자 역할이다

A 에서 B 로 데이터가 직접 전달이 아닌 delegate 라는 대리자를 통해
​A -> delegate -> B

이런 식으로 데이터가 전달된다

​① : SetTextDelegate 라는 함수에게 delegate 선언

② : BeginInvoke 로 대리자 비동기식 실행

③ SetTextDelegate 라는 delegate 의 인수에

textBox1.AppendText 를 실행하는 함수 지정

위 단계를 거쳐 진행이 되고, 최종적으로 실행이 되는 폼의 모양을 확인하자 * 아두이노의 시리얼 모니터가 꺼져 있어야 함 켜져 있으면 포트가 겹쳐 실행 안됨


[ 실행된 폼 ]

이렇게 실행된 폼을 활용해 컴퓨터에 구성된 포트 중 선택할 수 있는 콤보박스, 비트 전송 속도 등 옵션을 선택할 수 있게 만들 수 있다 👍


​ 🌞 앞서 말했던 지정 옵션 빼보기

private void Form1_Load(object sender, EventArgs e)
        {
            ComPort.PortName = "COM3";          // 아두이노가 연결된 포트 번호
            ComPort.BaudRate = 9600;            // 일반적으로 9600
            ComPort.DataBits = 8;               // 통신의 비트 설정 : 일반적으로 8비트
            ComPort.Parity = Parity.None;       // 통신의 정확도를 위한 패리티 검사(1의 개수의 짝/홀 여부)
            ComPort.StopBits = StopBits.One;    // 통신 말미 1로 지정
            ComPort.Handshake = Handshake.None;
            ComPort.Open();
            ComPort.DiscardInBuffer();          // 수신 버퍼 데이터 삭제
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            ComPort.Close();                    // 시리얼 포트 닫음
            ComPort.Dispose();                  // 시리얼 포트 완전 해제
            ComPort = null;
        }

마지막으로 요 부분에 들어있는 시리얼 포트 세팅 옵션들 { databits, parity, stopbits, handshake } 을 빼서 실행해보자

잘 실행된다 :D


(전체 코드 구성)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;                          // 포트 설정을 위한 호출

namespace Serial_to_LCD
{
    public partial class Form1 : Form
    {
        SerialPort ComPort = new SerialPort();  // 전역변수 ComPort 설정
        private delegate void SetTextDelegate(string getString);

        public Form1()
        {
            InitializeComponent();
            ComPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
        }

        private void DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string rxd = ComPort.ReadTo("\n");
            this.BeginInvoke(new SetTextDelegate(SerialReceived), new object[] { rxd });
        }

        private void SerialReceived(string inString)
        {
            textBox1.AppendText(inString + "\r\n");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ComPort.PortName = "COM3";          // 아두이노가 연결된 포트 번호
            ComPort.BaudRate = 9600;            // 일반적으로 9600
            //ComPort.DataBits = 8;               // 통신의 비트 설정 : 일반적으로 8비트
            //ComPort.Parity = Parity.None;       // 통신의 정확도를 위한 패리티 검사(1의 개수의 짝/홀 여부)
            //ComPort.StopBits = StopBits.One;    // 통신 말미 1로 지정
            //ComPort.Handshake = Handshake.None;
            ComPort.Open();
            ComPort.DiscardInBuffer();          // 수신 버퍼 데이터 삭제
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            ComPort.Close();                    // 시리얼 포트 닫음
            ComPort.Dispose();                  // 시리얼 포트 완전 해제
            ComPort = null;
        }

        
    }
}

회고) delegate 의 존재에 대해 이해는 쉬웠지만 코드 구성에 대한 구체적인 이해가 조금 부족했다

포트로 전송되는 데이터를 받는 방법에 있어 이벤트 핸들러 외에도 여러 방법이 있었는데 이 부분에 대해 알아보는데 시간이 더 필요할 것 같다

databits, parity, stopbits, handshake 등 최근엔 옵션 지정 자체가 잘 쓰이지 않는다는 것 같은데 이 부분은 인터넷에 있는 정보 외에도 실무에서 직접 겪어보면서 알아보면 좋을것 같다(옵션은 알아두자)