콘텐츠로 이동
무료 평가 시작

Modbus TCP

Modbus TCP는 PDU 앞에 7바이트 MBAP(Modbus Application Protocol) 헤더를 붙여 TCP 연결로 보냅니다. 직렬 회선과 달리 CRC가 없습니다. 오류 검출은 TCP가 맡습니다. 이 페이지는 MODBUS Messaging on TCP/IP Implementation Guide V1.0b의 §3.1 프로토콜 설명, §3.1.3 MBAP 헤더, §4.2 TCP 연결 관리를 근거로 합니다.

+------------------+------------------+----------+------------+-------------------+
| 트랜잭션 식별자 2B | 프로토콜 식별자 2B | 길이 2B | 유닛 식별자 1B | PDU |
+------------------+------------------+----------+------------+-------------------+
00 01 00 00 00 06 01 03 00 00 00 01
필드 설명
트랜잭션 식별자(transaction identifier) 클라이언트가 정하고 서버가 응답에 그대로 복사합니다. 응답이 어느 요청의 것인지 짝을 맞춥니다.
프로토콜 식별자(protocol identifier) Modbus는 항상 0입니다.
길이(length) 뒤따르는 바이트 수입니다. 유닛 식별자 1바이트와 PDU 길이를 더한 값입니다.
유닛 식별자(unit identifier) 게이트웨이 뒤의 장비를 고를 때 씁니다. 아래에서 설명합니다.

위 요청에 대한 응답은 00 01 00 00 00 05 01 03 02 00 2A입니다. 트랜잭션 식별자 1과 유닛 식별자 1이 그대로 돌아오고, 길이는 유닛 식별자와 응답 PDU 4바이트를 더한 5입니다.

TCP는 바이트 스트림이므로 한 번의 수신에 ADU의 일부만 오거나 여러 ADU가 이어서 올 수 있습니다. 수신 측은 길이 필드로 프레임 경계를 찾습니다. PDComm은 받은 바이트를 하나씩 디코더에 넣어 프레임을 조립합니다. 프로토콜 식별자가 0이 아니거나 길이가 허용 범위(2~254) 밖이면 연결을 닫습니다.

IP 주소가 이미 장비를 가리키므로 TCP 장비에 직접 보낼 때는 유닛 식별자가 필요하지 않습니다. 구현 가이드는 이때 0xFF를 쓰라고 권하고, 0도 받아들일 수 있다고 설명합니다. 반면 TCP와 직렬 버스를 잇는 게이트웨이 앞에서는 유닛 식별자가 직렬 버스의 장비 주소를 고릅니다.

실제 장비는 다음 중 하나로 동작합니다. 장비 매뉴얼에서 어떤 값을 요구하는지 확인합니다.

  • 유닛 식별자를 무시하고 모든 요청에 응답합니다.
  • 정해진 값(보통 1 또는 0xFF)만 받습니다.
  • 게이트웨이처럼 값마다 다른 장비로 전달합니다.

PDComm 서버의 동작: PDComm TCP 서버는 TCP에 직접 연결된 장비 하나입니다. 구성의 unit 값(기본 1)과 직접 주소 지정 값 0xFF, 0에 응답합니다. TCP에서 0은 브로드캐스트가 아니므로 응답이 돌아옵니다. 응답에는 요청의 유닛 식별자가 그대로 들어갑니다. 그 밖의 값(예: 2)은 예외 0A(Gateway Path Unavailable)를 돌려줍니다. PDComm 게이트웨이는 routes에 있는 유닛 식별자만 직렬 버스로 전달하고, 나머지는 0xFF와 0을 포함해 예외 0A로 답합니다. PDComm 클라이언트는 구성의 unit을 쓰고, request 명령의 unit으로 요청마다 다른 값(0~255)을 보낼 수 있습니다.

  • Modbus TCP의 표준 포트는 502이고, Modbus/TCP Security는 802입니다.
  • 이 문서의 튜토리얼은 1502와 1802를 씁니다. 실제 장비 서비스와 겹치지 않고, Linux에서 관리자 권한 없이 열 수 있습니다.
  • 서버 주소를 127.0.0.1:0처럼 포트 0으로 지정하면 운영체제가 빈 포트를 고릅니다. PDComm은 connect 응답의 config.address에 실제 주소를 알려 줍니다.
  • PDComm의 address는 IP:포트 형식의 소켓 주소입니다. 192.168.0.10:502, [::1]:1502처럼 씁니다. localhost:1502 같은 호스트 이름은 받지 않으며 connect가 Invalid TCP address로 실패합니다.

PDComm의 연결 관리는 다음과 같습니다.

항목 동작
서버 동시 연결 최대 32개입니다. 그보다 많은 연결은 바로 닫습니다.
서버 유휴 연결 120초 동안 데이터가 없으면 연결을 닫습니다.
클라이언트 연결 시간 connect는 timeout_ms 안에 연결되지 않으면 실패합니다.
클라이언트 요청 request 한 번에 요청 하나를 보내고 응답이나 timeout_ms를 기다립니다.
클라이언트 시간 초과 응답이 timeout_ms 안에 오지 않으면 Timeout 오류를 돌려주고 연결을 닫습니다. 늦게 온 응답이 다음 요청의 응답으로 잘못 짝지어지지 않게 하기 위해서입니다. 다시 connect한 뒤 계속합니다.
쓰기 재전송 자동으로 다시 보내지 않습니다. 쓰기가 시간 초과되면 값을 먼저 읽어 실제로 적용됐는지 확인합니다.

게이트웨이(gateway)는 Modbus TCP 요청을 받아 RS-485 같은 직렬 버스의 장비로 전달하고, 응답을 다시 TCP로 돌려줍니다. 유닛 식별자가 직렬 장비를 고릅니다.

TCP 클라이언트 --(유닛 1)--> 게이트웨이 --(주소 7)--> 직렬 장비 A
--(유닛 2)--> --(주소 9)--> 직렬 장비 B

직렬 버스는 한 번에 거래 하나만 처리합니다. 여러 TCP 클라이언트의 요청은 게이트웨이 안에서 차례를 기다립니다. 게이트웨이가 요청을 전달하지 못하면 두 가지 예외로 알립니다.

예외 상황
0A Gateway Path Unavailable 유닛 식별자에 해당하는 경로가 없습니다.
0B Gateway Target Device Failed to Respond 직렬 장비가 시간 안에 응답하지 않았습니다.

PDComm 게이트웨이는 routes 표로 TCP 유닛 식별자를 직렬 주소에 명시적으로 연결합니다. 실습은 튜토리얼: TCP-직렬 게이트웨이에서 합니다.