C#

C# Modbus TCP 통신 뚫기 : NModbus 라이브러리 사용

우준세 2024. 10. 3. 21:20
728x90
반응형

 

자동화나 제어 시스템에 널리 사용되고 있는 Modbus 프로토콜은 

제어 모듈과 통신할 때 많이 사용되고 있는 프로토콜입니다.

 

이번 포스팅은 Modbus TCP에 대해 간략히 설명하고 

C#에서 Modbus를 사용하는 방법과 NModbus 라이브러리를 활용하는 방법에 대해 작성했습니다.


| Modbus TCP 란?

Modbus는 산업 자동화 및 제어 시스템에서 널리 사용되는 프로토콜로 많이 사용되고 있습니다.

장치 간 간의 데이터 전송에 사용되고 있으며

Modbus TCP란 Modbus 프로토콜의 변형으로 TCP/IP 네트워크를 통해 작동합니다.

 

이 프로토콜은 클라이언트 - 서버 아키텍처 기반으로 작동되며 

클라이언트(마스터)는 서버(슬레이브)에게 요청을 보내고 서버는 요청에 대한 응답을 제공합니다.

 

| Modbus TCP 주요 특징

  • 단순성 : 구현이 간단하며 다양한 장치와 시스템에 쉽게 사용
  • 호환성 : TCP/IP 네트워크 기반으로, 기존의 네트워크 인프라를 활용
  • 확장성 : 여러 장치와의 통신이 가능하여 대규모 시스템에도 유용

위와 같은 내용으로 Modbus TCP는 주로 센서 모듈과 통신할 때나 장비 제어, 모니터링 등으로

데이터를 통신할 때 많이 사용하고 있습니다.


| C#에서 Modbus 사용법 (직접 구현)

C#에서 Modbus 통신을 하기 위해서는 직접 구현하는 방법도 있지만 

Modbus 프로토콜 자체는 데이터를 패킷 하여 송수신하는 작업을 포함하기 때문에 

직접 구현하기보다는 라이브러리를 사용하는 것이 효율적입니다 

 

만약 직접 구현한다고하면 소켓 프로그래밍을 통해 직접 프로토콜을 구현해야 합니다.

직접 프로토콜을 구현하는 간단한 기본 단계와 코드로 구현해 보겠습니다.

 

| Modbus 기본 단계

  1. 소캣 생성 : TCP 소캣 생성
  2. 서버 연결 : Modbus 슬레이브 연결
  3. 요청 패킷 생성 : 프로토콜에 맞는 요청 패킷 생성
  4. 패킷 전송 : 생성한 요청 패킷을 슬레이브에 전송
  5. 응답 수신 : 슬레이브로부터 응답 패킷 수신
  6. 응답 처리 : 수신한 응답을 해석하여 필요 데이터 추출
  7. 연결 종료 : 통신 종료 후 소켓 닫기

예제) 소켓 프로그래밍을 사용한 Modbus TCP 통신 

// Modbus 슬레이브의 IP 주소와 포트
string ipAddress = "192.168.0.100";
int port = 502;

// TCP 소켓 생성
using (TcpClient client = new TcpClient(ipAddress, port))
{
    NetworkStream stream = client.GetStream();

    // Modbus 요청 패킷 생성 (Function Code: 0x03 - Read Holding Registers)
    byte[] request = new byte[12];
    request[0] = 0; // Transaction Identifier (2 bytes)
    request[1] = 0; // Transaction Identifier
    request[2] = 0; // Protocol Identifier (2 bytes)
    request[3] = 0; // Protocol Identifier
    request[4] = 0; // Length (2 bytes)
    request[5] = 6; // Length (6 bytes for the rest of the message)
    request[6] = 1; // Unit Identifier (슬레이브 ID)
    request[7] = 3; // Function Code (Read Holding Registers)
    request[8] = 0; // Starting Address (High byte)
    request[9] = 1; // Starting Address (Low byte)
    request[10] = 0; // Quantity of Registers (High byte)
    request[11] = 1; // Quantity of Registers (Low byte)

    // 요청 패킷 전송
    stream.Write(request, 0, request.Length);
    Console.WriteLine("요청 패킷 전송 완료.");

    // 응답 수신
    byte[] response = new byte[256];
    int bytesRead = stream.Read(response, 0, response.Length);
    Console.WriteLine("응답 패킷 수신 완료.");

    // 응답 처리
    if (bytesRead > 0 && response[7] == 3) // Function Code가 3인지 확인
    {
        // 데이터는 9번째 바이트부터 시작
        int registerValue = (response[9] << 8) + response[10]; // 레지스터 값 조합
        Console.WriteLine($"레지스터 값: {registerValue}");
    }
    else
    {
        Console.WriteLine("유효하지 않은 응답입니다.");
    }
}

 

TcpClient를 사용하여 Modbus 슬레이브에 연결하고 

Function Code 0x03을 사용한 홀딩 레지스터를 사용하여 요청 패킷을 생성했습니다.

stream.Write()를 사용하여 요청 패킷을 전송하였으며 

stream.Read()를 통해 응답을 받습니다. 

 

위 코드 예제는 Modbus TCP 통신을 구현할 때 가장 기본적인 흐름을 보여주는 예제이므로 

실제 환경에서 쓰신다면 에러 처리나 데이터를 처리할 때 기능을 추가해야 합니다! 


| C#에서 Modbus 사용법 (NModbus 라이브러리 사용)

직접 구현할 때는 저렇게 데이터 처리나 TCP Client 연결 같은 부분을 신경을 다 써줘야 하지만

NModbus 라이브러리를 사용하면 효율적으로 코드 구현을 할 수 있습니다.

 

예제) NModbus를 사용한 Modbus TCP 통신

Modbus 기본 단계를 따르지만 앞서 직접 구현했던 코드보다 훨씬 간략하게 구현할 수 있습니다. 

 

1) Modbus TCP 클라이언트 생성

var tcpClient = new TcpClient("192.168.1.100", 502); 
var factory = new ModbusFactory();
var modbusMaster = factory.CreateMaster(tcpClient);

 

TcpClient는 Modbus 서버와의 TCP 연결을 설정하며,

ModbusFactory는 IModbusMaster 객체를 생성하여 실제로 Modbus 명령을 송수신할 수 있게 해 줍니다.

 

2) 레지스터 읽기

// 1번 슬레이브 장치의 주소 100번부터 시작하는 5개의 레지스터를 읽습니다.
ushort[] registers = modbusMaster.ReadHoldingRegisters(1, 100, 5);

foreach (var register in registers)
{
    Console.WriteLine($"Register Value: {register}");
}

 

Modbus 프로토콜에서 데이터를 읽는 기본 단위는 Register인데 

특정 주소의 데이터를 읽으려면 ReadHoldingRegisters() 메서드를 사용합니다.

위 코드에서는 슬레이브 1번 ID의 장치의 레지스터 주소 100부터 5개의 레지스터를 읽어

registers 변수에 출력하는 예제입니다. 

 

3) 레지스터 쓰기

ushort[] valuesToWrite = { 10, 20, 30, 40, 50 };
modbusMaster.WriteMultipleRegisters(1, 100, valuesToWrite);

 

반대로 데이터를 쓰려면 WriteMultipleRegisters 메서드를 사용하는데

이는 여러 개의 레지스터에 한 번에 값을 쓰는 메서드입니다.

위 코드는 슬레이브 ID 1번 장치의 레지스터 100번부터 5개의 값을 씁니다.

 

4) 연결 종료

tcpClient.Close();

 

작업이 끝나면 TCP 연결을 종료하여 리소스를 해제해야 합니다 (꼭!) 

 


| 마무리 

NModbus 라이브러리를 사용해서 간단하게 Modbus TCP 통신을 구현했습니다.

저도 장비 업체에 있으면서 온도 컨트롤러나 전력량계 같은 장치들과 통신을 할 때 자주 쓰고 있고 

 

특히 중요한 점은 예외 처리를 잘하여 응답을 받지 못해도 코드가 죽지 않게 만드는 게 중요한 것 같습니다! 

 

틀린 점이나 질문이 있으시면 댓글로 남겨주세요!

 

감사합니다 :) 

728x90
반응형