Modbus Function Code란? FC01·FC02·FC03·FC04부터 Read·Write 명령까지
Modbus Function Code란? FC01·FC02·FC03·FC04부터 Read·Write 명령까지#
1장 Modbus의 Function Code는 무엇을 의미할까#
Modbus 장비에 다음 Packet을 보냈다고 하겠습니다.
01 03 00 00 00 02 C4 0B처음 보면 숫자의 나열처럼 보입니다.
하지만 Field를 나누면 의미가 달라집니다.
01
→ Device Address
03
→ Function Code
00 00
→ Starting Address
00 02
→ Quantity
C4 0B
→ CRC여기서 가장 중요한 값 중 하나가:
03입니다.
0x03은 Modbus에서 Read Holding Registers를 의미합니다.
즉 이 Packet은 개념적으로:
Device 1에게 Holding Register 0번부터 두 개를 읽어 달라.
라는 요청입니다.
Modbus에서 Function Code는 결국 장비에게 무엇을 해달라고 요청하는지를 나타내는 명령 번호입니다. 공식 Modbus Application Protocol은 각 Function Code와 Request·Response 형식을 정의합니다.
2장 Function Code는 데이터 영역과 연결된다#
Modbus의 기본 Data Model에는 다음 네 영역이 있습니다.
Coil
Discrete Input
Holding Register
Input Register그리고 대표적인 Function Code는 이 영역과 연결됩니다.
| Function Code | 이름 | 대상 | 동작 |
|---|---|---|---|
01 |
Read Coils | Coil | 읽기 |
02 |
Read Discrete Inputs | Discrete Input | 읽기 |
03 |
Read Holding Registers | Holding Register | 읽기 |
04 |
Read Input Registers | Input Register | 읽기 |
05 |
Write Single Coil | Coil | 1개 쓰기 |
06 |
Write Single Register | Holding Register | 1개 쓰기 |
15, 0x0F |
Write Multiple Coils | Coil | 여러 개 쓰기 |
16, 0x10 |
Write Multiple Registers | Holding Register | 여러 개 쓰기 |
따라서 Modbus Packet을 볼 때는 Address만 봐서는 부족합니다.
Function Code
+
Starting Address를 같이 봐야 어떤 Data에 접근하는지 알 수 있습니다.
3장 FC01과 FC02로 1Bit 상태 읽기#
FC01 Read Coils#
Function Code 01은 Coil을 읽습니다.
Coil은 1Bit Read/Write 영역입니다.
예를 들어 장비 제조사가 다음처럼 Mapping했다고 하겠습니다.
Coil 0
→ Gate Open Command
Coil 1
→ Warning Lamp
Coil 2
→ BuzzerFC01 Request는 개념적으로 다음 정보를 포함합니다.
Function
Starting Address
Quantity예:
01 01 00 00 00 08 ...의미를 단순화하면:
Device 01
Function 01
Read Coils
Starting Address
0
Quantity
8입니다.
FC02 Read Discrete Inputs#
Function Code 02는 Discrete Input을 읽습니다.
Discrete Input은 1Bit Read Only 영역입니다.
예:
Discrete Input 0
→ 차량 감지
Discrete Input 1
→ Gate Fully Open
Discrete Input 2
→ Gate Fully Closed따라서 주차관제 장비에서 Sensor 상태를 읽는 용도로 Mapping할 수 있습니다.
단, 어떤 Sensor가 어느 Address에 연결되는지는 Modbus가 아니라 제조사가 결정합니다.
여러 Bit는 Byte 안에 Packing된다#
8개의 Coil 상태가 다음과 같다고 하겠습니다.
Coil 0 = 1
Coil 1 = 0
Coil 2 = 1
Coil 3 = 0
Coil 4 = 0
Coil 5 = 0
Coil 6 = 0
Coil 7 = 0응답에서는 이 상태들이 하나의 Byte 안에 Bit 단위로 Packing될 수 있습니다.
그래서 FC01·FC02 응답을 분석할 때는 단순히 Byte 값만 읽는 것이 아니라 각 Bit 위치를 풀어서 해석해야 합니다.
4장 FC03과 FC04로 16Bit Register 읽기#
FC03 Read Holding Registers#
Function Code 03은 Holding Register를 읽습니다.
예:
01 03 00 00 00 02 C4 0B이를 나누면:
01
Device Address
03
Read Holding Registers
00 00
Starting Address = 0
00 02
Quantity = 2
C4 0B
CRC입니다.
즉 Holding Register 0번부터 두 개를 읽는 요청입니다.
FC04 Read Input Registers#
Function Code 04는 Input Register를 읽습니다.
공식 사양에서는 한 요청으로 1개에서 125개의 연속된 Input Register를 읽을 수 있으며, Register 하나는 응답에서 2Byte로 전달됩니다. Register Address는 PDU 안에서 0부터 시작합니다.
예를 들어:
Function
04
Starting Address
0008
Quantity
0001이면 Input Register Address 8에서 한 개를 요청합니다.
응답은 개념적으로:
04
02
00 0A처럼 구성될 수 있습니다.
04
→ Function
02
→ Byte Count
00 0A
→ Register Value입니다.
Register 개수와 Byte Count를 구분한다#
Register 두 개를 요청했다고 하겠습니다.
Quantity
0002Register 하나는 2Byte이므로 정상적인 Data 부분은:
2 Registers × 2 Bytes
=
4 Bytes입니다.
따라서 Response의 Byte Count는:
04가 됩니다.
이 관계를 알면 Packet이 중간에서 잘렸는지 판단하는 데 도움이 됩니다.
5장 FC05와 FC06으로 하나의 값을 쓰기#
FC05 Write Single Coil#
Function Code 05는 Coil 하나에 값을 씁니다.
표준에서는 Coil ON을:
FF 00OFF를:
00 00으로 표현합니다.
예:
01 05 00 00 FF 00 8C 3A구조:
01
→ Device Address
05
→ Write Single Coil
00 00
→ Coil Address 0
FF 00
→ ON
8C 3A
→ CRC입니다.
실제 장비에서 Coil 0이 무엇을 의미하는지는 Register Map을 확인해야 합니다.
정상 응답은 요청을 Echo한다#
FC05가 정상적으로 처리되면 Server는 Function Code, Address와 값을 포함한 요청 내용을 Response로 다시 반환하는 구조를 사용합니다.
즉:
Request
01 05 00 00 FF 00 ...
Response
01 05 00 00 FF 00 ...처럼 보일 수 있습니다.
하지만 이것은 Modbus Write 요청이 정상 처리됐다는 Protocol 응답이지, 차단기나 Motor 같은 물리 장치가 최종 위치까지 움직였다는 의미는 아닙니다.
FC06 Write Single Register#
Function Code 06은 Holding Register 하나를 씁니다.
예:
01 06 00 02 00 0A CRC구조:
01
→ Device
06
→ Write Single Register
00 02
→ Register Address 2
00 0A
→ Value 10입니다.
FC06 역시 정상 응답은 요청 내용을 Echo하는 형태입니다.
6장 FC15와 FC16으로 여러 값을 한 번에 쓰기#
Decimal Function Code 15는 Hexadecimal로:
0x0F이며 Write Multiple Coils입니다.
Function Code 16은:
0x10이며 Write Multiple Registers입니다.
FC15 Write Multiple Coils#
여러 Coil을 한 번에 변경합니다.
개념적인 Request 구조:
Function Code
Starting Address
Quantity of Outputs
Byte Count
Output Values예를 들어 여러 Relay 상태를 한 번에 변경해야 하는 경우 사용할 수 있습니다.
하지만 실제 물리 장치를 제어하는 Coil이라면 여러 Output을 한꺼번에 변경하는 것이 안전한지 별도로 검토해야 합니다.
FC16 Write Multiple Registers#
여러 Holding Register를 연속해서 씁니다.
개념적으로:
Function
10
Starting Address
Quantity
Byte Count
Register Values형태입니다.
예를 들어 32Bit 값을 두 Register에 저장하는 Device라면 FC16을 사용해 두 Register를 함께 쓸 수 있습니다.
여러 Register를 쓴다고 업무 Transaction까지 보장되는 것은 아니다#
주의해야 합니다.
Modbus Function이 정상 Response를 반환했다고 해서:
여러 업무 상태 변경이
Database Transaction처럼 처리된다.고 일반화해서는 안 됩니다.
장비 내부에서 실제 값을 어떻게 적용하고 저장하는지는 Device 구현에 달려 있습니다.
중요한 설정을 여러 Register에 걸쳐 변경한다면 제조사 Manual에서 적용 시점과 저장 동작을 확인하는 편이 안전합니다.
7장 Function Code와 실제 장비 동작을 혼동하면 안 된다#
주차관제 Gate를 예로 들어보겠습니다.
제조사가:
Coil 0
→ Gate Open Command라고 정의했다고 하겠습니다.
Application이:
FC05
Address 0
ON을 보냅니다.
Modbus Response도 정상입니다.
그렇다면 Gate가 완전히 열렸을까요?
아직 알 수 없습니다.
실제로는:
FC05 Write
↓
Modbus 정상 Response
↓
Controller가 Command 처리
↓
Safety Sensor 확인
↓
Motor 동작
↓
Gate 이동
↓
Open Sensor ON같은 과정이 있을 수 있습니다.
따라서 안전한 시스템에서는 실제 상태를 별도의 Discrete Input이나 Register로 다시 확인하는 것이 좋습니다.
Command
→ Coil
실제 상태
→ Discrete Input같이 분리하는 방식입니다.
8장 Exception Response를 읽으면 장애 원인이 보인다#
Modbus Server가 Request를 처리할 수 없으면 정상 Response 대신 Exception Response를 보낼 수 있습니다.
Exception Response에서는 원래 Function Code의 최상위 Bit를 1로 설정합니다.
예를 들어:
정상 Function
05에서 Exception이 발생하면:
85가 됩니다.
즉:
05 + 80
=
85입니다.
그 뒤에 Exception Code가 따라옵니다.
대표적인 Exception Code#
공식 Modbus 사양의 대표적인 Exception Code는 다음과 같습니다.
| Exception Code | 이름 | 의미 |
|---|---|---|
01 |
Illegal Function | 지원하지 않거나 현재 상태에서 허용되지 않는 Function |
02 |
Illegal Data Address | 요청한 Address 또는 Address 범위가 유효하지 않음 |
03 |
Illegal Data Value | Request Data 구조나 값이 Protocol상 허용되지 않음 |
04 |
Server Device Failure | 요청 처리 중 Server 내부에서 복구하기 어려운 오류 발생 |
05 |
Acknowledge | 일부 장시간 Programming 명령을 받아 처리 중 |
06 |
Server Device Busy | 장시간 작업 등으로 현재 요청을 처리할 수 없음 |
특히 03 Illegal Data Value는:
Register에 저장하려는 업무 값이
현실적으로 이상하다.는 뜻으로 단순 해석하면 안 됩니다.
Protocol Request의 구조나 Quantity 같은 값이 허용 범위를 벗어났다는 의미일 수 있습니다.
Exception Response 예#
Request:
01 05 00 10 FF 00 ...Server가 Function 자체를 지원하지 않는다고 가정합니다.
응답 PDU는 개념적으로:
85 01이 될 수 있습니다.
85
→ FC05 Exception Response
01
→ Illegal Function입니다.
9장 Function Code별 장애를 이렇게 구분한다#
FC01·FC02에서 값이 이상하다#
확인:
Starting Address
Quantity
Bit 위치
Manufacturer Mapping여러 Bit가 Byte 안에 Packing되므로 Bit 위치를 잘못 읽었는지도 확인합니다.
FC03·FC04에서 값이 이상하다#
확인:
Register Address
Register Count
Signed / Unsigned
Scale
Byte Order
Word Order입니다.
Response가 정상이라고 실제 해석값까지 정상인 것은 아닙니다.
FC05·FC06이 실패한다#
확인:
Write 가능한 영역인가?
Function을 장비가 지원하는가?
Address가 맞는가?
값이 Protocol 형식에 맞는가?
Device가 현재 Write 가능한 상태인가?입니다.
FC15·FC16이 실패한다#
추가로:
Quantity 제한
Byte Count
전체 요청 길이
연속 Address 지원 여부를 확인합니다.
10장 RTU와 TCP에서도 Function Code 자체는 이어진다#
Modbus RTU와 Modbus TCP는 전달 Frame 구조가 다르지만 Application PDU의 Function Code 개념은 공유합니다.
Modbus RTU#
[Device Address]
[Function Code]
[Data]
[CRC]Modbus TCP#
[MBAP Header]
[Function Code]
[Data]Modbus TCP에서는 MBAP Header에 Transaction Identifier, Protocol Identifier, Length, Unit Identifier 등이 들어갑니다.
일반적인 Modbus TCP는 TCP Port 502를 사용합니다.
RTU의 CRC를 TCP Frame에 그대로 붙이는 것은 아니다#
Modbus RTU:
Address
Function
Data
CRCModbus TCP:
MBAP
Function
Data입니다.
따라서 단순하게:
RTU Packet
+
TCP Header가 되는 것이 아닙니다.
다만 Serial-to-Ethernet 장비가 Transparent Mode로 단순 Byte Stream만 전달한다면 TCP 안에 RTU Frame 자체가 그대로 지나가는 별도의 구조도 있을 수 있습니다.
Gateway Mode를 확인해야 하는 이유입니다.
11장 실제 HEX Packet을 분석하는 순서#
처음 보는 Modbus Frame이 있다고 하겠습니다.
01 03 00 64 00 02 CRC바로 Register 값을 추측하지 않습니다.
다음 순서로 봅니다.
1. Device 또는 Unit 확인#
012. Function Code 확인#
03
→ Read Holding Registers3. Starting Address 확인#
00 64
→ 1004. Quantity 확인#
00 02
→ 2 Registers5. Response 예상#
Register 두 개이므로 Data는:
2 × 2 Bytes
=
4 Bytes를 예상할 수 있습니다.
이렇게 Function Code를 먼저 해석하면 뒤에 오는 Field의 의미도 자연스럽게 결정됩니다.
Response도 같은 방식으로 읽는다#
예:
01 03 04 00 19 00 64 CRC구조:
01
→ Device
03
→ Read Holding Registers Response
04
→ Byte Count
00 19
→ 첫 번째 Register
00 64
→ 두 번째 Register입니다.
이제 Application에서:
25
100이라는 값을 얻을 수 있습니다.
실제 의미는 Manufacturer Register Map을 확인해야 합니다.
12장 Function Code 분석에서 가장 흔한 실수#
FC01과 FC05를 혼동한다#
FC01
→ Coil 읽기
FC05
→ Coil 하나 쓰기입니다.
FC03과 FC04를 같은 Register라고 생각한다#
FC03
→ Holding Register
FC04
→ Input Register입니다.
Address가 똑같아도 다른 Data 영역입니다.
FC15와 FC16을 Hex 값으로 착각한다#
문서에서:
15
16이라고 Decimal로 적을 수도 있습니다.
Hexadecimal로는:
FC15
→ 0x0F
FC16
→ 0x10입니다.
Protocol 문서를 볼 때 Decimal인지 Hex인지 확인해야 합니다.
Exception Code 01을 일반적인 통신 실패라고 생각한다#
01은 표준적으로 Illegal Function입니다.
통신선이 끊겼다는 의미가 아닙니다.
Exception이 왔는데 계속 Retry한다#
예:
Exception 02
Illegal Data Address라면 같은 Address를 계속 다시 요청한다고 해결되지 않습니다.
Register Map을 확인해야 합니다.
Write 정상 Response를 실제 동작 완료로 생각한다#
FC05나 FC06 Response가 정상이어도 실제 Motor·Relay·Gate의 물리적 상태는 별도로 확인해야 합니다.
13장 현장에서 Function Code를 진단하는 순서#
Modbus 통신은 되는데 원하는 Data가 나오지 않는다면 다음 순서로 확인합니다.
1. RTU인가 TCP인가?
↓
2. Device Address / Unit ID가 맞는가?
↓
3. Function Code가 맞는가?
↓
4. Starting Address가 맞는가?
↓
5. Quantity가 허용 범위인가?
↓
6. Response Function Code가 같은가?
↓
7. Exception Response인가?
↓
8. Byte Count가 예상과 같은가?
↓
9. Data Type이 맞는가?
↓
10. Byte·Word Order가 맞는가?
↓
11. 실제 장비 상태와 일치하는가?이렇게 보면:
Network 문제
Protocol 문제
Address 문제
Data 해석 문제
실제 장비 문제를 서로 분리할 수 있습니다.
14장 현장 체크리스트#
□ 요청 Function Code를 확인했는가?
□ 장비가 해당 Function Code를 지원하는가?
□ Coil·Discrete Input·Holding Register·Input Register를 구분했는가?
□ Starting Address가 맞는가?
□ 0-based·1-based 표기 차이를 확인했는가?
□ Quantity가 맞는가?
□ Response Byte Count가 예상과 같은가?
□ Exception Response 여부를 확인했는가?
□ Exception Code의 정확한 의미를 확인했는가?
□ 16Bit Register를 어떤 Data Type으로 해석하는가?
□ 32Bit 값이라면 Register 두 개 이상을 읽었는가?
□ Byte Order와 Word Order를 확인했는가?
□ FC05·FC06 Write 후 실제 장비 상태를 다시 확인하는가?
□ FC15·FC16의 다중 쓰기가 장비에서 지원되는가?
□ RTU라면 CRC와 Serial 설정을 확인했는가?
□ TCP라면 MBAP·Unit ID·TCP 연결 상태를 확인했는가?15장 자기 점검#
FC01과 FC02의 차이는 무엇인가#
FC01은 Coil을 읽고 FC02는 Discrete Input을 읽습니다. 둘 다 Bit 단위 Data를 읽지만 대상 Data 영역이 다릅니다.
FC03과 FC04의 차이는 무엇인가#
FC03은 Holding Register를, FC04는 Input Register를 읽습니다.
FC05와 FC06은 무엇을 쓰는가#
FC05는 Coil 하나를 쓰고 FC06은 Holding Register 하나를 씁니다.
0x85 Response는 무엇을 의미하는가#
원래 Function Code 0x05 요청에서 Exception이 발생했다는 의미입니다. 뒤에 따라오는 Exception Code를 확인해야 원인을 알 수 있습니다.
FC05 응답이 정상이라면 차단기가 실제로 열렸다는 의미인가#
아닙니다. Modbus Request가 정상 처리되었다는 의미와 실제 물리적 장비 상태 변화는 구분해야 합니다.
16장 이 글을 마치며#
Modbus Function Code는 복잡해 보이지만 기본 구조는 단순합니다.
무엇을 읽거나 쓸 것인가?
↓
Function Code
어디를 대상으로 할 것인가?
↓
Starting Address
얼마나 처리할 것인가?
↓
Quantity
결과는 무엇인가?
↓
Response 또는 Exception대표적인 Function Code를 다시 정리하면:
FC01
→ Read Coils
FC02
→ Read Discrete Inputs
FC03
→ Read Holding Registers
FC04
→ Read Input Registers
FC05
→ Write Single Coil
FC06
→ Write Single Register
FC15 / 0x0F
→ Write Multiple Coils
FC16 / 0x10
→ Write Multiple Registers그리고 오류가 발생하면:
Original Function Code
+
0x80
↓
Exception Response형태로 판단할 수 있습니다.
특히 다음 다섯 가지를 기억하면 됩니다.
Function Code는 Modbus 장비에 어떤 동작을 요청하는지 결정합니다.
Function Code와 Address를 함께 봐야 실제로 어떤 Data 영역을 읽고 쓰는지 알 수 있습니다.
FC01·02는 Bit 영역을, FC03·04는 16Bit Register 영역을 읽으며 FC05·06·15·16은 쓰기 작업에 사용됩니다.
Exception Response가 돌아왔다면 무조건 Retry하지 말고 Illegal Function·Illegal Data Address·Illegal Data Value 등의 원인을 먼저 확인해야 합니다.
Write Response가 정상이라고 실제 기계 동작까지 완료됐다고 판단해서는 안 되며 Sensor나 상태 Register를 통해 최종 상태를 확인해야 합니다.
결국 Modbus Function Code를 읽는다는 것은 숫자 01, 03, 10을 외우는 것이 아닙니다.
HEX Packet에서 Function Code를 찾아 어떤 Data 영역에 어떤 작업을 요청했는지 복원하고, Response와 Exception을 통해 장비가 그 요청을 어떻게 처리했는지 추적하는 것이 핵심입니다.