글 / Modbus·OPC UA

Modbus 32비트 값의 바이트 순서

레지스터 내부의 바이트 순서는 사양이 규정하지만 두 레지스터의 순서는 장비마다 다르므로 직접 확인해야 한다.

작성일 · 읽는 시간 2분 · Modbus Application Protocol V1.1b3 기준

문제 정의

Modbus 레지스터는 16비트이므로 32비트 정수나 실수는 레지스터 두 개에 분할하여 저장한다. 사양은 레지스터 하나를 상위 바이트부터 전송한다는 것까지만 규정하고, 32비트 값을 두 레지스터에 분할하는 방법은 규정하지 않는다. 따라서 어느 레지스터가 상위 워드(값의 상위 16비트)인지는 장비마다 다르다.

바이트 순서의 종류

값 123456(0x0001E240)의 네 바이트를 차례로 A B C D라고 하면 저장 방법은 네 가지이다.

이름 수신 바이트 레지스터 두 개 의미
ABCD 00 01 E2 40 0x0001, 0xE240 상위 워드 먼저
CDAB E2 40 00 01 0xE240, 0x0001 하위 워드 먼저
BADC 01 00 40 E2 0x0100, 0x40E2 워드 내부의 바이트 순서를 반전
DCBA 40 E2 01 00 0x40E2, 0x0100 전체 바이트 순서를 반전

워드 순서만 다른 ABCD와 CDAB가 우선 확인할 후보이다.

네 가지 바이트 순서로 레지스터 두 개에 놓이는 바이트 배치

struct를 이용한 두 순서 비교

pythondecode.py
import struct

regs = [0x0001, 0xE240]  # 장비에서 읽은 레지스터 두 개


def u32(regs: list[int], word_swap: bool = False) -> int:
    hi, lo = (regs[1], regs[0]) if word_swap else regs
    # 두 레지스터를 4바이트로 이어 붙인 뒤 32비트로 다시 푼다
    return struct.unpack(">I", struct.pack(">HH", hi, lo))[0]


print(u32(regs))                  # 123456
print(u32(regs, word_swap=True))  # 3795845121

실수는 ">I"를 ">f"로 변경하여 해석한다. 123.456은 0x42F6, 0xE979로 저장되고, 순서를 반대로 해석하면 약 -1.88e25가 된다.

순서 확인 방법

설명서에 순서가 명시되어 있지 않으면 값을 이미 알고 있는 레지스터를 읽어 두 가지 방법으로 해석한다. 하나는 올바른 값이고 다른 하나는 비정상적인 값이다.

같은 레지스터 두 개를 두 워드 순서로 해석한 결과 비교

두 레지스터는 하나의 요청으로 함께 읽는다. 별도의 요청으로 읽으면 그 사이에 값이 변경되어 상위와 하위가 서로 다른 시점의 값이 될 수 있다.

참고

pymodbus 3.8.1부터 convert_from_registers는 word_order 인자("big", "little")로 워드 순서를 선택한다.