본문 바로가기

개발/PLC

Modbus TCP 통신 원리: PLC 서버와 Python 클라이언트의 데이터 요청·응답 구조

반응형

반응형

Modbus TCP 통신 구조를 이해해야 하는 이유?

PLC에서 생산량이나 설비 상태를 읽어 MES에 저장하려면 PLC와 외부 프로그램 사이의 통신 구조를 이해해야 합니다. PLC 내부에 생산량 120이라는 값이 저장되어 있더라도 외부 프로그램이 해당 데이터를 자동으로 읽을 수 있는 것은 아닙니다. 어떤 네트워크 주소로 연결해야 하는지, 어떤 통신 프로토콜을 사용하는지, 데이터를 읽을 주소는 무엇인지 확인해야 합니다.

Modbus TCP는 이러한 데이터 교환에 사용할 수 있는 산업용 통신 프로토콜입니다. Modbus는 PLC와 계측기, 전력량계, 온도 조절기 등 다양한 산업용 장치에서 활용됩니다.

Modbus TCP는 기존 Modbus 데이터 교환 방식을 TCP/IP 네트워크에서 사용할 수 있도록 구성한 방식입니다. 웹 개발자에게 TCP/IP 통신은 익숙할 수 있지만, Modbus TCP는 HTTP API와 다르게 동작합니다.

HTTP에서는 URL과 요청 메서드 등을 이용해 기능을 호출하는 반면, Modbus TCP에서는 기능 코드와 데이터 주소를 이용해 장치의 값을 읽거나 씁니다. 이번 글에서는 실제 PLC 없이 Python 가상 PLC를 구현하기 위한 통신 구조를 정리합니다.


Modbus TCP 서버와 클라이언트의 역할

Modbus TCP에서는 일반적으로 데이터를 제공하는 장치가 서버이고, 데이터를 요청하는 프로그램이 클라이언트입니다. PLC가 Modbus TCP 서버 기능을 제공한다면 Python 프로그램에서 클라이언트로 접속할 수 있습니다.

[PLC 또는 가상 PLC]
Modbus TCP 서버
192.168.0.100:502
         ▲
         │
     TCP 연결
         │
         ▼
[Python 수집 프로그램]
Modbus TCP 클라이언트

 

 

 

 

클라이언트는 서버에 특정 데이터를 읽어 달라고 요청합니다. 서버는 요청을 해석하고, 해당 데이터가 제공 가능한 주소라면 값을 반환합니다.

 

예를 들어 Holding Register 주소 1에 생산량이 저장되어 있다고 가정하겠습니다. Python 클라이언트는 해당 주소의 값을 읽도록 요청합니다.

PLC는 현재 저장된 값인 120을 응답으로 반환합니다. 이때 서버가 전달하는 것은 생산량이라는 이름이 아니라 레지스터에 저장된 데이터입니다.생산량이라는 의미는 별도의 레지스터 맵과 프로그램 설정을 통해 해석합니다. 따라서 Modbus TCP 클라이언트를 구현하려면 통신 연결 정보와 데이터 정의를 모두 알아야 합니다.


Modbus TCP의 IP 주소와 포트 설정

Modbus TCP는 TCP/IP 네트워크에서 동작하므로 서버의 IP 주소와 포트가 필요합니다. Modbus TCP의 표준 포트는 502입니다.

실제 PLC가 다음과 같이 설정되어 있다고 가정하겠습니다.

IP 주소: 192.168.0.100
포트: 502

 

 

 

 

클라이언트는 해당 주소로 TCP 연결을 시도합니다. 하지만 연결에 성공했다고 해서 레지스터 조회까지 정상적으로 동작한다는 뜻은 아닙니다.

 

연결 이후에는 장치가 Modbus TCP 요청을 정상적으로 처리하는지 확인해야 합니다. PLC에서 Modbus TCP 기능이 비활성화되어 있거나 요청한 주소를 지원하지 않는다면 통신 오류가 발생할 수 있습니다.

네트워크 연결 자체가 실패하는 경우에는 IP 주소, 포트, 방화벽, 네트워크 경로 등을 확인해야 합니다. 이번 시리즈에서는 가상 PLC를 같은 컴퓨터에서 실행할 예정이므로 다음 주소를 사용할 계획입니다.

서버 주소: 127.0.0.1
서버 포트: 5020

 

 

 

 

127.0.0.1은 현재 컴퓨터 자신을 의미하는 루프백 주소입니다. 5020은 학습용으로 선택한 포트입니다. Modbus TCP의 표준 포트는 502이며, 5020은 표준 포트가 아닙니다. 가상 PLC를 다른 컴퓨터에서 실행한다면 127.0.0.1 대신 해당 컴퓨터의 네트워크 주소를 사용해야 합니다.

 


Modbus TCP 요청 메시지의 기본 구조

Modbus TCP에서는 요청과 응답에 정해진 메시지 구조를 사용합니다. 대표적으로 MBAP 헤더와 PDU로 구성됩니다. MBAP는 Modbus Application Protocol Header의 약자입니다. PDU는 Protocol Data Unit으로, 실제 Modbus 기능 코드와 관련 데이터를 포함합니다.

Modbus TCP 요청

┌─────────────────────┐
│ MBAP Header         │
├─────────────────────┤
│ Transaction ID      │
│ Protocol ID         │
│ Length              │
│ Unit Identifier     │
├─────────────────────┤
│ Modbus PDU          │
├─────────────────────┤
│ Function Code       │
│ Start Address       │
│ Quantity            │
└─────────────────────┘

 

 

 

 

Transaction ID는 요청과 응답을 연결하는 데 사용합니다. Protocol ID는 일반적인 Modbus TCP 통신에서 0으로 설정됩니다. Length는 뒤따르는 메시지 길이를 나타냅니다.

 

Unit Identifier는 장치를 식별하는 필드로, 특히 TCP와 직렬 Modbus 장치를 연결하는 게이트웨이 환경에서 중요합니다. Function Code는 수행할 작업을 나타냅니다.

Start Address는 조회를 시작할 데이터 주소이며, Quantity는 읽을 데이터 개수입니다. 모든 Modbus 기능 코드가 동일한 PDU 필드 구조를 사용하는 것은 아닙니다. 위 구조는 여러 레지스터를 읽는 요청을 중심으로 단순화한 예시입니다.


Holding Register 읽기 요청의 실제 데이터 구조

Holding Register를 읽을 때는 기능 코드 03을 사용합니다.

예를 들어 주소 1부터 3개의 레지스터를 읽는다고 가정하겠습니다.

기능 코드: 03
시작 주소: 1
조회 개수: 3

 

 

 

 

이 요청에 대한 정상 응답은 다음과 같은 값들을 포함할 수 있습니다.

 

HR1 = 120
HR2 = 115
HR3 = 5

 

 

 

 

응답에는 읽어온 레지스터 값이 순서대로 포함됩니다. 여기서 한 가지 주의할 점이 있습니다. Modbus 통신의 기능 코드와 데이터 주소는 서로 다른 값입니다.

 

기능 코드 03은 Holding Register를 읽겠다는 뜻이고, 주소 1은 조회할 위치를 의미합니다. 기능 코드 03을 사용했다고 해서 레지스터 주소 3을 조회하는 것은 아닙니다.

또한 일반적인 Holding Register 읽기 요청에서는 한 번에 최대 125개의 레지스터를 조회할 수 있습니다. 실제 PLC가 허용하는 최대 개수는 이보다 적을 수 있으므로 장비 문서를 확인해야 합니다.


Modbus TCP 요청 패킷 예시

이번에는 실제 요청 메시지가 어떤 형태인지 살펴보겠습니다. Holding Register 주소 1부터 3개를 읽는 요청을 예로 들겠습니다. 다음은 Transaction ID가 1이고 Unit Identifier가 1인 Modbus TCP 요청의 16진수 표현입니다.

00 01  00 00  00 06  01  03  00 01  00 03

 

 

 

 

각 부분의 의미는 다음과 같습니다.

 

00 01  Transaction ID = 1
00 00  Protocol ID = 0
00 06  Length = 6
01     Unit Identifier = 1
03     Function Code = 3
00 01  Start Address = 1
00 03  Quantity = 3

 

 

 

 

이 메시지를 받은 서버가 정상적으로 요청을 처리하면 주소 1부터 3까지의 값을 응답합니다. 예를 들어 값이 120, 115, 5라면 다음과 같은 응답이 가능합니다.

 

00 01 00 00 00 09 01 03 06 00 78 00 73 00 05

 

 

 

 

응답을 구분하면 다음과 같습니다.

 

00 01  Transaction ID = 1
00 00  Protocol ID = 0
00 09  Length = 9
01     Unit Identifier = 1
03     Function Code = 3
06     데이터 바이트 수 = 6

00 78  120
00 73  115
00 05  5

 

 

 

 

 

 

 

 

 

Holding Register 하나는 2바이트이므로 세 개의 값을 읽으면 데이터 부분은 총 6바이트입니다. 실제 프로그램에서는 이러한 바이트를 직접 조립하지 않고 Modbus 라이브러리를 사용할 수 있습니다. 하지만 요청과 응답의 구조를 이해하면 통신 오류를 분석하거나 패킷을 확인할 때 도움이 됩니다.

 


Modbus TCP와 HTTP API의 차이

라라벨 개발자는 HTTP API를 이용해 데이터를 주고받는 방식에 익숙합니다. 예를 들어 특정 설비의 생산량을 조회하기 위해 Laravel API를 호출할 수 있습니다.

GET /api/machines/1/production

 

 

 

 

라라벨에서는 라우트와 컨트롤러를 통해 요청을 처리하고 JSON으로 응답할 수 있습니다. Modbus TCP에서는 이러한 URL 구조를 사용하지 않습니다. 대신 데이터 영역과 주소를 지정하여 값을 읽습니다.

 

Modbus TCP

Function Code = 03
Address = 1
Count = 1

 

 

 

 

두 방식은 서로 다른 프로토콜이지만, 데이터를 요청하고 응답받는다는 공통점이 있습니다. 따라서 Python 수집기가 Modbus TCP로 PLC 데이터를 읽은 다음 Laravel API에 HTTP 요청을 보내는 구조를 만들 수 있습니다.

 

[PLC]
   │
   │ Modbus TCP
   ▼
[Python 수집기]
   │
   │ HTTP
   ▼
[Laravel API]
   │
   ▼
[MySQL]

 

 

 

 

이러한 구조를 이용하면 PLC와 MES 사이의 통신 역할을 분리할 수 있습니다.

 


Modbus TCP 통신 오류는 어떻게 발생할까?

Modbus TCP 통신에서는 다양한 오류가 발생할 수 있습니다.

첫 번째는 TCP 연결 실패입니다.

서버 IP 주소가 잘못되었거나 네트워크가 연결되지 않았을 때 발생할 수 있습니다.

두 번째는 응답 시간 초과입니다.

요청을 전송했지만 정해진 시간 안에 응답을 받지 못한 상황입니다.

세 번째는 Modbus 예외 응답입니다.

서버가 요청을 수신했지만 처리할 수 없는 기능이나 주소인 경우 예외 응답을 반환할 수 있습니다. 예를 들어 지원하지 않는 기능 코드나 유효하지 않은 레지스터 주소를 요청한 경우입니다.

정상 응답
요청한 레지스터 값 반환

예외 응답
지원하지 않는 기능 또는 주소 등의 오류

시간 초과
정해진 시간 안에 응답 없음

연결 실패
TCP 연결 자체가 성립하지 않음

 

 

 

이러한 상황은 각각 원인과 처리 방법이 다릅니다. 따라서 수집 프로그램에서는 단순히 값이 없다는 결과만 저장하기보다 통신 실패와 실제 측정값 0을 구분해야 합니다. 특히 PLC의 생산량이 0인 경우와 통신이 끊어진 경우를 동일하게 처리하면 MES에서 잘못된 생산현황을 표시할 수 있습니다.

 


Python 가상 PLC를 사용하는 이유?

실제 산업용 PLC를 사용하려면 장비와 통신 환경을 준비해야 합니다. 기종에 따라 PLC 본체뿐 아니라 전원 장치, 통신 모듈, 센서 등이 필요할 수 있습니다.

개인이 Modbus TCP의 기본 원리를 이해하기 위해 처음부터 모든 장비를 구매하는 것은 부담이 될 수 있습니다. 이번 시리즈에서는 Python으로 Modbus TCP 서버를 구현하여 실제 PLC의 통신 동작을 모의합니다.

가상 PLC는 레지스터 공간에 임의의 값을 저장하고, 클라이언트가 해당 값을 읽도록 응답합니다. 이렇게 하면 실제 장비가 없어도 IP 주소, 포트, 기능 코드, 레지스터 주소, 요청과 응답의 구조를 확인할 수 있습니다.

다만 가상 PLC는 실제 센서 입력이나 산업용 안전 제어를 재현하지 않습니다. 이후 ESP32와 센서를 활용한 실습을 진행하더라도 ESP32를 산업용 PLC와 동일한 장비로 취급하지는 않습니다.


Modbus TCP 실습 개발환경

이번 시리즈의 개발환경은 다음과 같습니다.

Python       3.13.2
PHP          8.2
Laravel      12
Database     MySQL
Protocol     Modbus TCP

PLC Server   Python 가상 PLC
Collector    Python
MES          Laravel
 

Modbus TCP 통신 구조: Python 가상 PLC와 라라벨 MES 데이터 수집 원리

MES 개발에서 Modbus TCP 통신을 이해해야 하는 이유?제조실행시스템(MES)을 개발할 때 설비에서 발생하는 생산량과 가동 상태를 자동으로 수집해야 하는 경우가 있습니다....

www.th-study.com

▲ Modbus TCP 데이터 수집 정리 : 파이썬과 라라벨로 구현 ▲

 

Python에서는 가상 PLC 서버와 Modbus TCP 클라이언트를 구현합니다. 라라벨에서는 수집 데이터를 저장하고 생산현황을 조회하는 기능을 구현할 예정입니다. 이번 편에서는 실제 통신 코드를 실행하지 않고 통신 메시지의 구조와 데이터 흐름을 이해하는 데 집중했습니다.

 


마무리

Modbus TCP는 산업용 장치가 TCP/IP 네트워크를 통해 데이터를 주고받도록 하는 통신 방식입니다. 클라이언트는 기능 코드와 레지스터 주소를 이용해 데이터를 요청하고 서버는 정상 응답이나 예외 응답을 반환합니다.

실제 MES 연동에서는 통신 연결 여부뿐 아니라 레지스터 주소, 자료형, 값의 의미, 통신 오류까지 고려해야 합니다. Python 가상 PLC를 이용하면 실제 장비 없이도 이러한 요청과 응답 구조를 학습할 수 있습니다.

이후에는 가상 PLC 서버를 실행하고 Python 클라이언트에서 레지스터를 직접 조회하여 이번 편에서 설명한 통신 원리를 검증할 예정입니다.

반응형