08. 모니터링 & 디버깅
06·07편에서 오가는 통신은 눈에 보이지 않는 바이트입니다. "안 되는데 왜 안 되는지 모르겠다"는 순간, 이 바이트를 직접 보고 손으로 해독하는 능력이 디버깅의 전부입니다. 실장비는 HRSS/Caterpillar Modbus Monitor로, PC는 Wireshark로 실제 Modbus/TCP 패킷을 캡처합니다. 그리고 하드웨어 없이도 연습하도록 16진 프레임을 사람이 읽게 풀어 주는 손해독기로 자동 검증합니다.
이 강의에서 배우는 것
- 1Modbus 패킷이 [Function Code][Data] 구조이며 모니터에 Address·FC·Data가 16진으로 표시됨을 안다
- 26종 FC(02h·01h·0Fh·04h·03h·10h)의 Master 송신/Slave 응답 포맷을 구분해 손으로 해독한다
- 3byte 수 규칙(Bit=⌈개수/8⌉ 올림, Word=개수×2)으로 응답 길이를 예측한다
- 4HRSS Modbus Monitor 절차와 Wireshark(loopback, Decode As Modbus/TCP)를 수행한다
소개
Modbus 패킷은 운반 방식(RTU vs TCP)에 따라 껍데기가 다르지만 알맹이(Slave Address + Function Code + Data)는 동일합니다. 모니터/Wireshark가 보여주는 것이 바로 이 알맹이입니다. TCP는 CRC가 없고(TCP 계층이 무결성 보장), RTU는 끝에 CRC 2바이트가 붙습니다.
핵심 개념
1) Function Code 6종과 byte 규칙
| FC | 의미 | 방향 |
|---|---|---|
| 02h | Read Discrete Inputs | 읽기(Bit) |
| 01h | Read Coils | 읽기(Bit) |
| 0Fh | Write Multiple Coils | 쓰기(Bit) |
| 04h | Read Input Registers | 읽기(Word) |
| 03h | Read Holding Registers | 읽기(Word) |
| 10h | Write Multiple Registers | 쓰기(Word) |
핵심 규칙 두 가지 — ① Bit 응답 byte 수 = ⌈개수/8⌉(올림), ② Word 응답 byte 수 = 개수×2. 읽기는 Master가 "어디서 몇 개"만 보내고 Slave가 데이터를 돌려주며, 쓰기(0Fh·10h)는 Master 송신에 데이터까지 실리고 Slave 응답은 시작주소+개수만 에코합니다.
2) 손계산 예제 3종 (매뉴얼 원문)
① 02 01 2C 00 05 → FC02, 시작주소 0x012C=300, 개수 5
② 04 04 00 5A 00 00 → FC04, byte 4(=2개), 값 [0x5A=90, 0]
③ 10 00 C9 00 03 06 00 6F 00 DE 01 4D
→ FC10, 시작주소 0x00C9=201, 개수 3, byte 6, 값 [111,222,333]
(Slave 응답은 10 00 C9 00 03 — 시작주소+개수만 에코)응답 FC의 최상위 비트가 1(예: 02h→82h)이면 에러 응답이고, 다음 1바이트가 Exception Code입니다(02h 잘못된 주소 / 03h 잘못된 값 / 01h 미지원 기능).
핵심 예제
16진 프레임을 입력하면 Function Code·시작주소·개수·byte수·데이터를 사람이 읽게 푸는 손해독기. 표준 라이브러리만 쓰고 포트가 필요 없어 모니터/Wireshark에서 본 byte를 그대로 검증합니다.
decode_frame("02 01 2C 00 05", "request") # FC=02h, 시작주소 300, 개수 5
decode_frame("04 04 00 5A 00 00", "response") # byte 4, 값 [90, 0]
decode_frame("10 00 C9 00 03 06 00 6F 00 DE 01 4D", "request") # 주소 201, 값 [111,222,333]# 💻 Wireshark 라이브 캡처(선택): Npcap + loopback 어댑터
# 1) python ../../_shared/robot_server_sim.py --port 1502
# 2) python ../../_shared/modbus_master.py --port 1502 read-di 0 8
# 3) 표시 필터 tcp.port == 1502, 비표준 포트는 Decode As → Modbus/TCP
# Query 02 00 00 00 08 / Response 02 01 08 (0x08=주소3만 On → [0,0,0,1,0,0,0,0])자주 하는 실수
Q. Wireshark에 패킷이 전혀 안 잡혀요(127.0.0.1↔127.0.0.1).
A. 로컬 통신은 물리 랜카드를 거치지 않습니다. 캡처 인터페이스를 Adapter for loopback traffic(Npcap Loopback)로 바꾸세요. 목록에 없으면 Wireshark 재설치 시 Npcap+loopback 옵션을 체크합니다.
Q. Protocol 컬럼이 Modbus/TCP가 아니라 TCP로만 나와요.
A. Wireshark는 502 포트만 Modbus로 자동 해석합니다. 실습은 1502이므로 패킷 우클릭 → Decode As… → 포트 1502를 Modbus/TCP로 지정하세요.
Q. byte 수와 레지스터 개수를 자꾸 헷갈려요.
A. Word는 1개당 2바이트입니다. 레지스터 개수 = byte수 ÷ 2(예: 04 04 ... → byte 4 → 2개). Bit는 반대로 ⌈개수/8⌉로 올림합니다.
정리
- Modbus 패킷의 알맹이는 [Function Code][Data], 16진수로 표시(HRSS 모니터 최대 200줄)
- 6종 FC: 읽기 02h/01h/04h/03h, 쓰기 0Fh/10h. 읽기 응답엔 데이터, 쓰기 응답엔 시작주소+개수 에코
- 길이 규칙: Bit 응답 byte=⌈개수/8⌉, Word 응답 byte=개수×2
- HRSS Modbus Monitor와 Wireshark(loopback, Decode As Modbus/TCP)로 실제 트래픽을 검증
과제
- 세 예제 프레임(02·04·10h)을 손해독기로 풀어 시작주소·개수·값을 확인
- 04 04 00 5A 00 00 의 레지스터 개수가 왜 2개인지 byte 규칙으로 설명
- Wireshark에서 read-di 8 트래픽을 캡처하고 Response 02 01 08 을 [0,0,0,1,0,0,0,0] 로 해독