Modbus 연결은 성공했는데 온도가 수만 도로 보이거나 전압이 아주 작은 소수로 표시되는 경우가 있습니다. 응답 바이트를 받았다는 것과 그 바이트를 올바른 물리량으로 해석했다는 것은 별개입니다.
먼저 레지스터 종류 → 주소 기준 → 자료형 → 바이트·워드 순서 → 배율 순서로 확인하세요. 이 글은 가상 레지스터를 사용하며 특정 제조사 센서의 실제 맵을 대신하지 않습니다.
40001과 프로토콜 오프셋 0을 구분하기
장치 문서의 40001은 첫 Holding Register를 나타내는 참조 표기일 수 있습니다. Modbus PDU에서는 레지스터 주소를 0부터 표현합니다. 따라서 문서의 첫 번째 레지스터를 읽기 위해 프로토콜 오프셋 0을 요청하는 구성이 흔합니다. 실제 프로그램 입력창이 0부터 세는지, 1부터 세는지, 문서의 참조 표기를 그대로 받는지는 따로 확인해야 합니다. Modbus Application Protocol V1.1b3, 4.4·6.3절
| 확인 항목 | 가상 센서 예시 |
|---|---|
| 문서 참조 표기 | Holding Register 40001 |
| 요청 함수 | 03, Read Holding Registers |
| 프로토콜 시작 오프셋 | 0 |
| 요청 수량 | 1개 |
| 응답 바이트 | 00 FA |
문서가 처음부터 프로토콜 오프셋을 제공한다면 다시 1을 빼면 안 됩니다. 숫자 하나만 기록하지 말고 “문서 주소 40001 / 프로토콜 오프셋 0 / 함수 03”처럼 주소의 의미를 같이 남깁니다.
함수 코드와 레지스터 폭 맞추기
Holding Register 읽기는 함수 03, Input Register 읽기는 함수 04를 사용합니다. 둘은 이름과 접근 방법이 다르므로 값이 안 나온다고 임의로 교환하지 않습니다. 장치 매뉴얼의 레지스터 종류를 먼저 확인합니다.
한 레지스터는 16비트이며 응답의 레지스터 내부에서는 상위 바이트가 먼저 나옵니다. 00 FA는 unsigned 정수로 250입니다. 문서가 단위 0.1°C를 정의했다면 25.0°C로 해석할 수 있습니다. 배율 0.1은 이 가상 센서의 약속이며 Modbus 자체의 공통 규칙은 아닙니다. Modbus 프로토콜의 데이터 인코딩·레지스터 읽기 설명
오류 응답과 정상 값도 분리하세요. Timeout이나 Illegal Data Address를 센서 값 0으로 저장하면 통신 실패가 정상 측정처럼 보이게 됩니다.
signed 정수와 float32를 구분하는 예제
아래 코드는 Python 3 표준 라이브러리만 사용합니다. 네트워크 요청이나 장치 쓰기는 수행하지 않습니다.
import struct
def checked_word(value):
if not 0 <= value <= 0xFFFF:
raise ValueError("register outside 16-bit range")
return value
def signed16(value):
value = checked_word(value)
return value - 0x10000 if value & 0x8000 else value
def float32_from_words(first, second, word_swap=False):
words = [checked_word(first), checked_word(second)]
if word_swap:
words.reverse()
raw = b"".join(word.to_bytes(2, "big") for word in words)
return struct.unpack(">f", raw)[0]
print(checked_word(0x00FA) * 0.1)
print(signed16(0xFF9C) * 0.1)
print(float32_from_words(0x41C8, 0x0000))
print(float32_from_words(0x0000, 0x41C8, word_swap=True))
예상 출력은 순서대로 25.0, -10.0, 25.0, 25.0입니다. 첫 두 값은 배율을 적용한 정수이고, 뒤 두 값은 IEEE 754 단정밀도 표현을 해석한 결과입니다. 실제 코드에서는 입력 자료형과 장치가 사용하는 결측·오류 코드도 검사해야 합니다.
레지스터 내부 바이트 순서와 두 레지스터의 순서
32비트 자료형은 레지스터 두 개를 조합할 수 있지만, 어떤 레지스터를 먼저 배치할지는 장치 문서에 따라야 합니다. 41C8 0000을 받는 장치와 0000 41C8을 받는 장치가 같은 25.0을 나타낼 수 있습니다. Modbus Organization도 제조사별 다중 레지스터 표현 차이를 설명합니다. 공식 Modbus 입문 문서의 floating-point 설명
위 Python 함수는 레지스터 내부는 big-endian으로 두고 두 워드만 교환하는 예제입니다. BADC·DCBA처럼 바이트까지 다른 방식으로 배치하는 장치의 모든 형식을 자동 판별하지 않습니다. 여러 변환을 시도해 그럴듯한 숫자가 나왔다는 이유만으로 형식을 확정하지 마세요.
문서의 알려진 시험값이나 장치 화면의 기준값과 비교합니다. 한 온도 지점만 확인하지 말고 양수·음수·소수 및 정상 범위의 여러 지점에서 확인하면 잘못된 해석을 발견하기 쉽습니다.
게이트웨이에는 원시 값과 품질 상태를 남기기
Modbus 값을 MQTT나 DB로 옮기는 게이트웨이를 설계할 때 다음 정보를 함께 남기는 방식을 고려할 수 있습니다.
- 맵 버전, 함수 코드, 시작 오프셋과 레지스터 수
- 원시 레지스터 배열, 적용한 자료형·순서·배율
- 해석 값과 단위, 정상·실패·오래된 값 여부
- 장치 측 측정 시각이 있으면 그 시각, 게이트웨이 수신 시각
예를 들어 응답이 끊겼을 때 이전의 25.0°C를 계속 표시할 수는 있지만, 새로 측정한 값처럼 현재 시각을 붙이면 안 됩니다. 마지막 정상 측정 시각과 stale 상태를 함께 보관해야 운영자가 데이터의 상태를 판단할 수 있습니다.
이 정보가 있으면 잘못된 배율을 수정한 뒤 원시 기록에서 다시 계산할 수 있고, 펌웨어 업데이트로 레지스터 맵이 바뀐 문제도 추적하기 쉽습니다. 저장량이 제한적이라면 모든 원시 데이터를 영구 저장하는 대신 오류 구간과 표본을 남기는 정책을 정할 수 있습니다.
현장에서 확인하는 순서
- 전원·배선·시리얼 조건 또는 IP·포트를 확인합니다.
- 장치 주소나 Unit ID와 읽기 함수를 맞춥니다.
- 문서 주소와 실제 요청 PDU의 오프셋을 비교합니다.
- 레지스터 수와 원시 HEX 응답을 확인합니다.
- 부호·다중 워드 순서·배율을 적용합니다.
- 장치 표시값이나 기준 입력과 비교한 결과를 기록합니다.
QModMaster 사용법으로 읽기 요청부터 확인할 수 있습니다. MQTT로 전달하는 단계에서는 중복 수신을 고려한 데이터 저장 설계도 함께 확인하세요.
작성·문서 확인: 2026년 9월 13일. 프로토콜 기준: Modbus Application Protocol V1.1b3. Python 예제 값과 경계값을 확인했으며 특정 실장치의 레지스터 맵 검증을 뜻하지 않습니다.