본문 바로가기
PLC 전기제어 기술자료/통신 기술자료

Modbus 입문 가이드 1편: 프로토콜 구조 이해 (RTU · TCP · 펑션코드)

by 위치결정JP 2026. 5. 22.
반응형

산업 자동화 현장에서 일하다 보면 Modbus 라는 이름을 끊임없이 마주칩니다.

인버터, 온도 컨트롤러, 유량계, 압력 트랜스미터, 스마트 센서 — 거의 모든 장비의 통신 사양서 첫 페이지에는 Modbus RTU 또는 Modbus TCP 가 적혀 있죠.

그런데 막상 구현을 시작하려고 하면 이런 의문들이 생깁니다.

RTU 와 TCP 는 뭐가 다른가?
FC03 과 FC04 는 왜 구분되어 있는가?
CRC-16 은 어떻게 계산하는가?
MBAP 헤더가 뭔가?

이 글에서는 Modbus 프로토콜의 구조부터 RTU 와 TCP 의 차이, 펑션코드 체계, 그리고 프레임 구성까지 한 번에 정리합니다.

1. Modbus 란 무엇인가

Modbus 는 1979년 Modicon (현재 Schneider Electric) 이 자사 PLC 간 통신을 위해 개발한 마스터-슬레이브 직렬 통신 프로토콜입니다.

공개 표준으로 전환된 이후 산업 자동화 분야의 사실상 표준(de facto standard) 으로 자리 잡았습니다.

지금도 수십 년 전 설계된 프로토콜이 현역으로 사용되는 이유는 단 하나입니다.

구조가 단순하기 때문입니다.

요청 한 번에 응답 한 번. 복잡한 핸드셰이크도 없고, 라이선스도 없습니다. 임베디드 환경에서 구현하기 쉽고, 디버깅도 쉽습니다.

Modbus 파생 표준

Modbus 는 전송 계층에 따라 세 가지 파생 표준으로 나뉩니다.

 

표준 전송 계층 특징
Modbus RTU RS-232 / RS-485 바이너리 프레임, CRC-16, 무신호 구간으로 프레임 경계 구분
Modbus ASCII RS-232 / RS-485 ASCII 인코딩, LRC 체크섬, : 시작 + CR LF 종료
Modbus TCP Ethernet (TCP/IP) MBAP 헤더 추가, CRC 없음, 다중 연결 가능

 

산업 현장에서는 RTUTCP 가 주력입니다. ASCII 는 구형 장비 호환 목적으로만 간헐적으로 등장합니다.

Modbus 메시지의 핵심은 PDU(Protocol Data Unit) 입니다. 어떤 전송 계층이든 "펑션코드 + 데이터" 라는 PDU 구조는 동일합니다.

 

시리얼 라인(RTU·ASCII)에서는 이 PDU 앞뒤로 슬레이브 주소오류 검출(CRC-16 / LRC) 이 감싸면서 하나의 완성된 프레임이 됩니다.

 

2. 마스터-슬레이브 구조

Modbus 는 철저한 마스터-슬레이브 구조로 동작합니다.

 

  • 마스터: 통신을 먼저 시작하는 쪽. PLC, HMI, SCADA 등
  • 슬레이브: 요청을 받아 응답하는 쪽. 인버터, 센서, 계측기 등

 

마스터 ─── 요청(Request) ───→ 슬레이브
마스터 ←── 응답(Response) ─── 슬레이브

 

슬레이브는 절대로 먼저 말을 걸지 않습니다. 마스터의 요청이 있어야만 응답합니다.

RTU 에서는 버스 1개에 마스터 1대, 슬레이브 최대 247대까지 연결할 수 있습니다.
TCP 에서는 IP 기반으로 이론상 제한 없이 연결 가능합니다.

3. 디바이스(레지스터) 영역 구분

Modbus 슬레이브 내부 데이터는 네 가지 영역으로 구분됩니다.

 

영역명 주소 표기 범위 접근 용도
Coil 0xxxx 00001~09999 R/W 디지털 출력. 마스터가 쓰고 읽음
Discrete Input 1xxxx 10001~19999 R 디지털 입력. 슬레이브 하드웨어 전용
Input Register 3xxxx 30001~39999 R 아날로그 입력·측정값. 슬레이브 전용
Holding Register 4xxxx 40001~49999 R/W 설정값·제어 파라미터. 범용

 

비트 단위 데이터는 Coil / Discrete Input 영역을, 16비트 워드 데이터는 Input Register / Holding Register 영역을 사용합니다.

표의 R/W · R 은 슬레이브 내부 메모리 속성입니다. 마스터는 항상 요청을 보내는 쪽이며, 슬레이브 메모리가 R/W 이면 마스터가 읽고 쓸 수 있다는 의미입니다.

한 가지 중요한 점이 있습니다.

프레임 내 실제 주소는 표기 주소 - 1 입니다 (0-based).

Holding Register 40001번지를 읽으려면 프레임에는 0x0000 을 넣어야 합니다.
40100번지는 0x0063 입니다.

단, 매뉴얼마다 표기 방식이 다릅니다. 40001 처럼 5자리 형식이면 1-based 이므로 1을 빼서 사용하세요. 0x0000 또는 0 으로 시작하는 형식이면 0-based 이므로 그대로 사용하면 됩니다.

 

💡 TIP

장비 매뉴얼을 받으면 데이터 영역(Coil / Register 종류)과 주소 체계(1-based / 0-based)를 가장 먼저 확인하는 습관을 들이세요. 이 두 가지만 정확히 맞춰도 통신이 안 될 때의 디버깅 시간을 크게 줄일 수 있습니다.

 

4. 펑션 코드 (Function Code)

펑션 코드는 무엇을 어떻게 읽고 쓸지 를 지정하는 명령어입니다.

 

FC 이름 동작 대상 영역 단위
01 Read Coils 읽기 Coil (0xxxx) 비트
02 Read Discrete Inputs 읽기 (전용) Discrete Input (1xxxx) 비트
03 Read Holding Registers 읽기 Holding Register (4xxxx) 16bit 워드
04 Read Input Registers 읽기 (전용) Input Register (3xxxx) 16bit 워드
05 Write Single Coil 1비트 쓰기 Coil (0xxxx) 비트
06 Write Single Register 1워드 쓰기 Holding Register (4xxxx) 16bit 워드
0F (15) Write Multiple Coils 다비트 쓰기 Coil (0xxxx) 비트
10 (16) Write Multiple Registers 다워드 쓰기 Holding Register (4xxxx) 16bit 워드

 

현장에서 가장 많이 사용하는 조합은 FC03 (Holding Register 읽기) 와 FC10 (Holding Register 다중 쓰기) 입니다.

FC03 과 FC04 — 둘 다 읽기인데 왜 구분되나?

처음 Modbus 를 공부할 때 가장 많이 혼동하는 부분입니다.

 

항목 FC03 FC04
대상 영역 Holding Register (4xxxx) Input Register (3xxxx)
접근 권한 R/W R (읽기 전용)
용도 설정값·제어 파라미터 측정값·센서 데이터
예시 인버터 주파수 설정값, PID 목표값 현재 전류값, 온도 센서 측정값

 

FC03 과 FC04 는 완전히 별개의 주소 공간입니다.

슬레이브 내부에서 40001번지와 30001번지는 다른 메모리 영역입니다. 같은 숫자처럼 보여도 전혀 다른 데이터가 들어 있습니다.

 

⚠️ 주의

장비 매뉴얼에 "Input Register" 로 표기된 주소는 반드시 FC04 로 읽어야 합니다. FC03 으로 읽으면 같은 번지여도 전혀 다른 영역의 데이터가 나옵니다. 통신은 정상인데 값이 이상할 때 가장 먼저 의심할 부분입니다.

 

실제 장비 매뉴얼은 펑션코드별 메시지 형식을 표로 제공합니다. 아래는 오토닉스 온도컨트롤러 매뉴얼의 데이터 읽기 펑션(FC04) 예시로, [국번][펑션코드][시작번지][데이터 갯수][CRC-16] 형식과 바이너리 송수신 예까지 보여줍니다.

 

5. Modbus RTU 프레임 구조

RTU 의 기본 프레임 구조는 단순합니다.

 

[ Slave Addr ][ Function Code ][ Data (N bytes) ][ CRC-Lo ][ CRC-Hi ]
    1 byte         1 byte                           1 byte    1 byte

 

FC03 요청 프레임 예시 — 슬레이브 01번, 0x006B번지부터 3워드 읽기

 

01  03  00 6B  00 03  76 87
│   │   └─────┘ └─────┘ └────┘
│   │   시작주소  읽기수  CRC-16
│   FC03
슬레이브01

 

FC03 응답 프레임 예시

 

01  03  06  02 2B  00 00  00 64  B9 12
│   │   │   └────────────────┘  └─────┘
│   │   │   데이터 (3워드 = 6바이트)  CRC-16
│   │   Byte Count (6)
│   FC03
슬레이브01

 

Byte Count = 요청 워드 수 × 2 입니다. 3워드를 읽었으니 6바이트.

RTU 메시지 프레임을 도식으로 보면, 슬레이브 주소와 펑션코드 뒤에 데이터가 오고 맨 끝에 CRC-16 이 붙는 구조가 한눈에 들어옵니다.

RTU 의 프레임 경계 — 무신호 구간

RTU 에서 프레임의 시작과 끝은 특정 문자가 아니라 무신호 구간으로 구분합니다.

 

─── 3.5T ───│ 01 03 00 6B 00 03 76 87 │─── 3.5T ───│ 다음 프레임 │
             └────── 요청 프레임 ──────┘

 

보레이트 기준 1바이트 전송 시간을 1 character time (1T) 이라 할 때:

 

  • 프레임과 프레임 사이: 3.5T 이상 무신호 → 프레임 경계
  • 같은 프레임 내 바이트 간격: 1.5T 이하 유지 (초과 시 프레임 폐기)

 

9600bps 기준으로 계산하면:

 

  • 1T = 1000ms ÷ (9600 ÷ 10) = 약 1.042ms
  • 3.5T = 약 3.645ms 이상 무신호 → 프레임 종료

 

프레임과 프레임 사이의 무신호 구간이 종료 기준이 되는 모습을 도식으로 보면 다음과 같습니다.

 

3.5T·1.5T 시간을 보레이트별로 계산한 표는 다음과 같습니다.

 

6. CRC-16 오류 검출

RTU 프레임 끝에는 반드시 CRC-16 이 2바이트 붙습니다. Low Byte 먼저, High Byte 나중 순서입니다.

CRC-16 의 파라미터는 다음과 같습니다.

 

항목
초기값 0xFFFF
다항식 0xA001

 

계산 절차를 의사코드로 표현하면 이렇습니다.

 

CRC = 0xFFFF

for each byte B in (슬레이브주소 ... 데이터 마지막 바이트):
    CRC = CRC XOR B            ← 하위 바이트에 현재 바이트 XOR

    for i = 0 to 7:
        if (CRC AND 0x0001) != 0:   ← LSB 검사
            CRC = (CRC SHR 1) XOR 0xA001
        else:
            CRC = CRC SHR 1

return CRC     ← Low Byte 먼저 프레임 끝에 추가

 

위 의사코드의 SHR (우시프트) 와 AND 0x0001 (bit0 검사) 두 가지는 Mitsubishi Q-series 에 그대로 존재하지 않습니다. 대신 우시프트 1비트 명령(SFR)이 시프트와 동시에 밀려난 bit0 을 시스템 캐리 비트(SM700)에 자동으로 저장하므로, 이 캐리 비트를 읽어서 분기에 사용합니다.

CRC 계산 결과를 수신 프레임의 CRC 와 비교한 다음, 일치 여부에 따라 데이터 처리 또는 재요청으로 갈립니다.

 

 [수신 완료]──[CRC 일치]────────────────( 데이터 처리 OK )

 [수신 완료]──[CRC 불일치]───────────────( 재요청 요구 )

 

구현 디테일(SFR·SM700 캐리 동작, 두 겹 루프 구조, 실제 적용 라이브러리)은 2편 — CRC-16 래더 구현과 수신 데이터 파싱 에서 상세히 다룹니다.

슬레이브로부터 응답이 돌아오면 동일한 방법으로 CRC 를 재계산해서 프레임 마지막 2바이트와 비교합니다. 불일치 시 수신 에러로 처리하고 재시도합니다.

예외 응답 (Exception Response)

슬레이브가 요청을 처리할 수 없을 때는 요청 FC | 0x80 값으로 응답합니다.

예를 들어 FC03 요청에 에러가 발생하면 응답 FC = 0x83 이 됩니다.

 

Exception Code 의미
01 지원하지 않는 펑션 코드
02 주소 범위 초과
03 데이터 값 오류
04 슬레이브 내부 오류
06 슬레이브 처리 중 — 재시도 필요

 

7. RTU vs TCP — 무엇이 다른가

RTU 와 TCP 는 같은 Modbus 명령 체계를 쓰지만 전송 계층이 완전히 다릅니다.

 

항목 Modbus RTU Modbus TCP
물리 계층 RS-232 / RS-485 Ethernet
포트 502 (IANA 표준)
프레임 헤더 슬레이브 주소 (1바이트) MBAP Header (6바이트)
프레임 경계 3.5T 무신호 구간 TCP 스트림 — Length 필드로 결정
오류 검출 CRC-16 (2바이트, 프레임 끝) TCP 자체 오류 검출 — CRC 없음
트랜잭션 ID 없음 MBAP 에 포함 (요청·응답 매칭용)
멀티마스터 불가 (버스 공유, 마스터 1대) 가능 (TCP 연결 다중화)
최대 슬레이브 RS-485 기준 최대 247대 IP 기반 — 이론상 제한 없음
연결 설정 없음 (버스 직결) TCP 3-way handshake 필요
재접속 처리 해당 없음 접속 끊김 시 재접속 시퀀스 필요

 

RTU 는 배선이 단순하고 장거리 RS-485 버스로 많은 장비를 묶을 수 있습니다.
TCP 는 기존 Ethernet 인프라를 그대로 활용하고, 다중 마스터 접속과 빠른 속도가 장점입니다.

8. Modbus TCP 프레임 구조 — MBAP 헤더

TCP 에서는 RTU 프레임 앞에 MBAP (Modbus Application Protocol) 헤더 6바이트가 추가됩니다.

 

[ Transaction ID ][ Protocol ID ][ Length ][ Unit ID ][ FC ][ Data ]
      2 byte           2 byte      2 byte    1 byte   1 byte  N byte
|<------------- MBAP Header (6 byte) ---------->|<----- PDU ------->|

 

필드 크기 내용
Transaction ID 2바이트 요청·응답 매칭 식별자. 마스터가 설정, 슬레이브가 그대로 반환
Protocol ID 2바이트 항상 0x0000 (Modbus 식별자 고정)
Length 2바이트 Unit ID 이후 바이트 수
Unit ID 1바이트 RTU 의 슬레이브 주소와 동일 역할

 

RTU 에 있던 CRC-16 은 TCP 에서 완전히 사라집니다. TCP 계층에서 오류 검출을 처리하기 때문입니다.

FC03 요청 프레임 예시 — TCP, 슬레이브 01번, 0x006B번지부터 3워드 읽기

 

00 01  00 00  00 06  01  03  00 6B  00 03
TXID   PROTO  LEN   UID FC  ADDR   QTY

 

Length = 0x0006 = Unit ID(1) + FC(1) + Start Addr(2) + Qty(2)

Transaction ID 의 역할

RTU 는 요청 1개에 응답 1개가 순서대로 오기 때문에 매칭이 자동으로 됩니다.

TCP 는 여러 요청을 동시에 보낼 수 있고, 응답 순서가 달라질 수 있습니다. Transaction ID 를 통해 어떤 요청에 대한 응답인지 매칭합니다.

요청마다 Transaction ID 를 1씩 증가시키고, 응답에서 동일한 ID 가 돌아오는지 확인하는 방식으로 구현합니다.

9. 레지스터 주소 체계 정리

현장에서 장비 매뉴얼을 보면 주소 표기 방식이 제각각이라 헷갈리는 경우가 많습니다.

핵심 규칙은 하나입니다.

5자리 표기(40001~) 매뉴얼: 표기 주소 - 1 = 프레임 주소
0x0000 표기 매뉴얼: 그대로 사용

 

매뉴얼 표기 FC 프레임 주소
40001 FC03 0x0000
40010 FC03 0x0009
40100 FC03 0x0063
30001 FC04 0x0000
00001 FC01 0x0000

 

또 한 가지 주의할 점은 FC별 1회 최대 읽기 수입니다.

 

FC 1회 최대
FC01 / FC02 (비트) 2000 비트
FC03 / FC04 (워드) 125 워드
FC10 (다중 쓰기) 123 워드

 

FC03 으로 한 번에 125워드 이상을 요청하면 슬레이브가 Exception Code 02 (주소 범위 초과) 로 응답합니다. 데이터가 많다면 나눠서 여러 번 요청해야 합니다.

 

⚠️ 주의

응답 프레임 크기는 항상 수신 버퍼 크기 안에 들어와야 합니다. 한 번에 읽는 워드 수가 많으면 Byte Count(데이터 바이트 수)가 커져 버퍼를 넘칠 수 있으므로, FC별 최대치(워드 125 / 비트 2000)와 실제 버퍼 크기 중 더 작은 값을 기준으로 분할하세요.

 

마무리

Modbus 는 오래된 프로토콜이지만, 단순함이 곧 경쟁력입니다.

 

  • RTU : RS-485 버스 기반, CRC-16 오류 검출, 무신호 구간으로 프레임 경계
  • TCP : Ethernet 기반, MBAP 헤더, CRC 없음, Transaction ID 로 요청·응답 매칭
  • FC03 vs FC04 : 완전히 별개 주소 공간 — 매뉴얼의 Register 종류를 반드시 확인
  • 주소 : 표기 주소 - 1 = 프레임 주소 (0-based)

 

이어지는 2편 — CRC-16 래더 구현과 수신 데이터 파싱 에서는 여기서 정리한 프레임 구조를 바탕으로, 미츠비시 Q 시리즈 PLC 에서 수신 데이터를 실제로 가공하는 과정(WTOB 분해 → 국번·FC 검증 → CRC-16 검증 → BTOW 복원)을 단계별로 다룹니다.

 

💡 TIP

사전정의 프로토콜(Predefined Protocol) 대신 송수신 버퍼를 직접 제어하는 무수순 방식으로 Modbus RTU 를 구현하는 전체 과정은 Q Modbus RTU 무수순 통신 구현 가이드 에서 별도로 정리했습니다. 프레임 구조를 이해했다면 다음 단계로 읽어보세요.

 

반응형