2016년 6월 20일 월요일

C# 간단한 시리얼 통신 라이브러리 소스코드 - simple c# source code for serial communication

 using System;  
 using System.Collections.Generic;  
 using System.Diagnostics;  
 using System.IO.Ports;  
 using System.Linq;  
 using System.Text;  
 using System.Threading;  
 using System.Threading.Tasks;  
   
 namespace SerialCommLib  
 {        
   public class SerialComm
   {    
     public delegate void DataReceivedHandlerFunc(byte[] receiveData);  
     public DataReceivedHandlerFunc DataReceivedHandler;  
   
     public delegate void DisconnectedHandlerFunc();  
     public DisconnectedHandlerFunc DisconnectedHandler;
   
     private SerialPort serialPort;  
      
     public bool IsOpen   
     {   
       get   
       {   
         if (serialPort != null) return serialPort.IsOpen;  
         return false;  
       }   
     }  
      
     // serial port check
     private Thread threadCheckSerialOpen;  
     private bool isThreadCheckSerialOpen = false;  
   
     public SerialComm()  
     {  
     }  
   
     public bool OpenComm(string portName, int baudrate, int databits, StopBits stopbits, Parity parity, Handshake handshake)  
     {  
       try  
       {  
         serialPort = new SerialPort();  
   
         serialPort.PortName = portName;  
         serialPort.BaudRate = baudrate;  
         serialPort.DataBits = databits;  
         serialPort.StopBits = stopbits;  
         serialPort.Parity = parity;  
         serialPort.Handshake = handshake;  
   
         serialPort.Encoding = new System.Text.ASCIIEncoding();  
         serialPort.NewLine = "\r\n";  
         serialPort.ErrorReceived += serialPort_ErrorReceived;     
         serialPort.DataReceived += serialPort_DataReceived; 
   
         serialPort.Open();  
   
         StartCheckSerialOpenThread();  
         return true;  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
         return false;  
       }  
     }  
   
     public void CloseComm()  
     {  
       try  
       {  
         if (serialPort != null)  
         {  
           StopCheckSerialOpenThread();  
           serialPort.Close();  
           serialPort = null;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     public bool Send(string sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData);  
           return true;  
         }          
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, 0, sendData.Length);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData, int offset, int count)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, offset, count);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     private byte[] ReadSerialByteData()  
     {  
       serialPort.ReadTimeout = 100;  
       byte[] bytesBuffer = new byte[serialPort.BytesToRead];  
       int bufferOffset = 0;  
       int bytesToRead = serialPort.BytesToRead;  
   
       while (bytesToRead > 0)  
       {  
         try  
         {  
           int readBytes = serialPort.Read(bytesBuffer, bufferOffset, bytesToRead - bufferOffset);  
           bytesToRead -= readBytes;  
           bufferOffset += readBytes;  
         }  
         catch (TimeoutException ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
   
       return bytesBuffer;  
     }  
   
     private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)  
     {  
       try  
       {  
         byte[] bytesBuffer = ReadSerialByteData();  
         string strBuffer = Encoding.ASCII.GetString(bytesBuffer);  

         if (DataReceivedHandler != null)  
           DataReceivedHandler(bytesBuffer);  
   
         Debug.WriteLine("received(" + strBuffer.Length + ") : " + strBuffer);     
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     private void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)  
     {  
       Debug.WriteLine(e.ToString());  
     }  
      
     private void StartCheckSerialOpenThread()  
     {   
       isThreadCheckSerialOpen = true;  
       threadCheckSerialOpen = new Thread(new ThreadStart(ThreadCheckSerialOpen));  
       threadCheckSerialOpen.Start();  
     }  
   
     private void StopCheckSerialOpenThread()  
     {  
       if (isThreadCheckSerialOpen)  
       {  
         isThreadCheckSerialOpen = false;  
         if (Thread.CurrentThread != threadCheckSerialOpen)  
           threadCheckSerialOpen.Join();
       }  
     }  
   
     private void ThreadCheckSerialOpen()  
     {  
       while (isThreadCheckSerialOpen)  
       {  
         Thread.Sleep(100);  
   
         try  
         {  
           if (serialPort == null || !serialPort.IsOpen)  
           {  
             Debug.WriteLine("seriaport disconnected");  
             if (DisconnectedHandler != null)  
               DisconnectedHandler();  
             break;  
           }  
         }  
         catch (Exception ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
     }  
   }  
 }  
   


< SerialComm 사용 >

using System.IO.Ports;

using SerialCommLib;
...
SerialComm serial = new SerialComm();
serial.DataReceivedHandler = DataReceivedHandler;
serial.DisconnectedHandler = DisconnectedHandler;
...
serial.OpenComm("COM3", 9600, 8, StopBits.One, Parity.None, Handshake.None);
...
serial.CloseComm();

private void DataReceivedHandler(byte[] receiveData)
{
    // do something with receiveData here
}

private void DisconnectedHandler()
{
    Console.WriteLine("serial disconnected");
}

* 라이브러리 사용은 시리얼 데이터 수신과 연결종료 이벤트 핸들러를 등록해서 콜백으로 받아서 처리하는 방식으로 간단하게 사용할 수 있다.

C# SerialPort 클래스는 이벤트 수신 방식과 쓰레드 방식 두 가지로 사용할 수 있는데 쓰레드 방식보다 이벤트 수신 방식이 더 간단하고 직관적이다.
SerialPort 클래스의 Read 계열 메소드는 블러킹 방식으로 동작하기때문에 데이터 수신 이벤트 발생시 반드시 BytesToRead 에 명시된 모든 바이트를 읽어서 수신버퍼를 비워줘야한다.


댓글 9개:

  1. 안녕하세요. 공개주신 라이브러리 감사히 보았습니다.
    현재 Readline() 명령에 latency가 있어서 다른 방법을 알아보는 중인데
    들어오는 데이터를 line 단위로 처리를 하려할때는 어찌하면 좋을까요?
    초면에 질문부터 드립니다. (__)

    답글삭제
  2. ReadLine 함수의 문제가 아니라 시리얼통신의 특징입니다.(송신데이터 그대로 수신되지않음) 수신데이터를 버퍼에 저장한후 앞에서부터 개행문자를 찾은다음 라인단위로 끊어서 처리해야합니다

    답글삭제
  3. 사용법에서 (해당 라이브러리를 사용하는 프로그램에서)
    serial.DataReceivedHandler = DataReceivedHandler; 를 하게 되면

    DataReceivedHandler'이(가) 현재 컨텍스트에 없습니다.
    serial이 현재 컨텍스트에 없습니다.

    오류가 나는지요 ?

    컴파일 환경 : vs2017 입니다.

    답글삭제
    답글
    1. DataReceivedHandler 함수 선언이 없어서입니다..

      삭제
    2. 제가 C# 문법에 대해 많이 모르는 상태에서 하려니 좀 .. 난감하네요..
      위 코드를 솔루션에 라이브러리 프로젝트로 하여 입력하였고,
      테스트 프로젝트를 새로 생성하여
      아래 코드 입력 (참조에는 IrLib 추가)

      serial.DataReceivedHandler = DataReceivedHandler;
      serial.DisconnectedHandler = DisconnectedHandler;

      빨간 밑줄 그어지면서 현재 컨텍스트에 없습니다. 출력됩니다.
      serial.DataReceivedHandler 와 DataReceivedHandler 모두 각각요....


      -- --------
      using System.Threading;
      using System.Threading.Tasks;
      using System.IO.Ports;
      using IrLib;

      namespace Test
      {
      class Program
      {
      SerialComm serial = new SerialComm();

      serial.DataReceivedHandler = DataReceivedHandler;
      serial.DisconnectedHandler = DisconnectedHandler;

      static void Main(string[] args)
      {
      string[] comList = System.IO.Ports.SerialPort.GetPortNames();


      foreach(string port in comList)
      {
      Console.WriteLine(port);
      }

      while(true)
      {
      Thread.Sleep(100);
      }
      }

      private void DataReceivedHandler(byte[] receiveData)
      {
      Console.WriteLine("serial DataReceived !!");
      }
      private void DisconnectedHandler()
      {
      Console.WriteLine("serial disconnected !!");
      }
      }
      }

      삭제
    3. serial.DataReceivedHandler = DataReceivedHandler;
      serial.DisconnectedHandler = DisconnectedHandler;
      부분을 Main 함수 안으로 넣고
      DataReceivedHandler, DisconnectedHandler 함수와 SerialComm serial 변수를 static 으로 변경해보세요

      삭제
    4. 제가 포트 연결을 잘못 했는가 봅니다.
      현재는 잘 동작하는 것 같습니다.
      덕분에 delegate 도 이해하게 되었고 감사합니다.

      삭제
  4. 안녕하세요 Serial Port 구현 하다가 질문이 생겼습니다.

    private void DataReceivedHandler(byte[] receiveData)
    {
    // do something with receiveData here
    }
    여기서 보시면 DataReceivedHandler를 통해 DataReceived 데이터를 Main으로 어떻게 보낼 수 있을까요

    답글삭제
    답글
    1. 해결했습니다. 간단한거였습니다.
      String msg= Encoding.ASCII.GetString(receiveData);

      삭제