Modbus float 워드 순서 문제, 빅엔디안·워드 스왑 해결법
요약
Modbus float 워드 순서는 32비트 실수 하나를 16비트 레지스터 2개에 나눠 담을 때 어느 워드와 바이트를 앞에 두는지 정한 배치 규칙입니다.
Modbus 규격은 레지스터를 16비트 단위로만 정하고 32비트 값을 합치는 순서는 정하지 않아서 장비와 수집 프로그램의 순서가 다르면 25.5℃가 -50,593,792나 2.36E-41처럼 엉뚱한 값으로 읽힙니다.
이 글은 같은 16진수 0x41CC0000(25.5)을 ABCD·CDAB·BADC·DCBA 네 가지 순서로 읽은 결과를 표로 비교하고 값이 1.2E-38처럼 0에 가깝게 나올 때 어디부터 확인하면 되는지 정리합니다.
새 온도 변환기를 Modbus로 연결하고 통신 표시등도 정상인데 화면에는 2.3603E-41이 찍히는 경우가 있습니다. 통신 오류 메시지는 없습니다. 장비는 질문에 제대로 답했고 받은 4바이트도 정확한데, 그 4바이트를 합치는 순서만 틀린 것입니다.
이 문제는 배선이나 통신 속도를 바꿔도 해결되지 않습니다. 수집 프로그램이나 게이트웨이에서 데이터형과 워드 순서 설정을 장비 사양서와 맞춰야 없어집니다. 원리를 알면 이상한 값의 모양만 보고도 원인을 꽤 정확히 짐작할 수 있습니다.
1. Modbus 32비트 float란?
Modbus 32비트 float는 IEEE 754 단정밀도(binary32) 형식의 실수 하나를 Holding Register나 Input Register 2개에 이어서 담은 값입니다. Modbus 레지스터 하나는 16비트(2바이트)라서 소수점이 있는 온도·압력·유량을 그대로 보내려면 레지스터 두 개가 필요합니다.
Modbus 규격은 레지스터 하나 안의 바이트를 상위 바이트부터 보내는 빅엔디안 방식으로 정해 두었습니다. 그런데 레지스터 두 개 중 어느 쪽이 32비트 값의 앞부분인지는 규격에 없어서 제조사마다 정하는 방식이 다릅니다. 함수 코드 03이나 04로 레지스터 2개를 읽는 과정은 똑같고, 받은 뒤 합치는 단계에서만 차이가 생깁니다. 함수 코드별 프레임은 Modbus 함수 코드 01~16 정리 글에 따로 정리되어 있습니다.
2. float 한 개는 레지스터 2개에 어떻게 담길까?
2-1. IEEE 754 단정밀도 구조
IEEE 754 단정밀도 float는 32비트를 부호 1비트, 지수 8비트, 가수 23비트로 나눕니다. 값은 (-1)^부호 × 1.가수 × 2^(지수-127)로 계산하고 지수 비트가 모두 0이면 0 또는 아주 작은 비정규수, 모두 1이면 무한대나 NaN(숫자 아님)이 됩니다.
| 구분 | 비트 수 | 위치 (32비트 중) | 역할 |
|---|---|---|---|
| 부호 | 1 | 맨 앞 1비트 | 0이면 양수, 1이면 음수 |
| 지수 | 8 | 다음 8비트 | 127을 뺀 값이 2의 거듭제곱 (정규수 -126~127) |
| 가수 | 23 | 나머지 23비트 | 1.xxx의 소수 부분, 유효숫자 약 7자리 |
부호와 지수가 모두 앞쪽 16비트(상위 워드)에 들어간다는 점을 기억해 두면 진단이 쉬워집니다. 상위 워드에 엉뚱한 레지스터가 들어가면 크기 자릿수가 통째로 바뀌기 때문입니다.
2-2. 계산 예시: 0x41CC0000은 왜 25.5일까?
- 2진수로 펼치기 : 0x41CC0000 = 0100 0001 1100 1100 0000 0000 0000 0000
- 부호 : 맨 앞 비트가 0이므로 양수입니다.
- 지수 : 다음 8비트 1000 0011 = 131, 131 - 127 = 4이므로 2^4 = 16을 곱합니다.
- 가수 : 나머지 비트 1001 1000…은 0.5 + 0.0625 + 0.03125 = 0.59375이고, 앞에 1을 붙이면 1.59375입니다.
- 결과 : 1.59375 × 16 = 25.5
장비가 25.5℃를 빅엔디안으로 보내면 첫 번째 레지스터는 0x41CC, 두 번째 레지스터는 0x0000이 됩니다. Modbus RTU로 함수 코드 03 응답을 받으면 데이터 부분이 바이트 수 04 뒤에 41 CC 00 00 순서로 들어옵니다. 첫 레지스터 하나만 16비트 정수로 읽으면 16844가 보이는데 이 숫자가 화면에 나온다면 데이터형을 float가 아닌 정수로 설정한 것입니다.
3. 워드 순서 4가지(ABCD·CDAB·BADC·DCBA) 차이는?
32비트 값을 앞에서부터 바이트 A·B·C·D로 부르면 레지스터 2개에 담는 방법은 네 가지가 됩니다. 장비 사양서나 수집 프로그램마다 부르는 이름이 달라서 같은 순서가 "Float Inverse", "Swapped", "Little-endian word order"처럼 다르게 적혀 있기도 합니다. 그래서 이름보다 바이트 문자 표기(ABCD 등)로 맞춰 보는 쪽이 정확합니다.
| 표기 | 흔히 부르는 이름 | 레지스터 1 (25.5일 때) | 레지스터 2 (25.5일 때) | 특징 |
|---|---|---|---|---|
| ABCD | 빅엔디안 | 0x41CC | 0x0000 | 상위 워드 먼저, 워드 안도 상위 바이트 먼저 |
| CDAB | 워드 스왑 | 0x0000 | 0x41CC | 하위 워드 먼저, 워드 안은 빅엔디안 유지 |
| BADC | 바이트 스왑 | 0xCC41 | 0x0000 | 워드 순서는 그대로, 각 워드 안의 바이트만 뒤집음 |
| DCBA | 리틀엔디안 | 0x0000 | 0xCC41 | 4바이트를 통째로 뒤집음 |
현장에서 가장 자주 만나는 순서는 ABCD와 CDAB입니다. 내부 CPU가 리틀엔디안인 장비가 레지스터 단위로만 빅엔디안을 지키면 CDAB가 나오기 쉽습니다. BADC와 DCBA는 워드 안의 바이트까지 뒤집은 경우라 규격상 드물지만 게이트웨이나 변환 프로그램을 한 번 더 거치면서 생기기도 합니다. 같은 문제는 32비트 정수(INT32·UINT32)에도 똑같이 생기고 64비트 double은 레지스터 4개를 쓰니 경우의 수가 더 늘어납니다.
4. 같은 16진수를 4가지 순서로 읽으면 값이 어떻게 달라질까?
장비가 25.5를 ABCD 순서로 보내서 레지스터 1 = 0x41CC, 레지스터 2 = 0x0000을 받았다고 하겠습니다. 수집 프로그램을 네 가지 순서로 바꿔 가며 설정하면 아래처럼 전혀 다른 숫자가 나옵니다. 받은 바이트는 똑같고 합친 순서만 다릅니다.
| 프로그램 설정 | 합친 32비트 | 화면에 나오는 값 | 해석 |
|---|---|---|---|
| ABCD | 0x41CC0000 | 25.5 | 장비와 순서가 같음 (정상) |
| CDAB | 0x000041CC | 약 2.3603E-41 | 상위 워드가 0x0000이 되어 비정규수 |
| BADC | 0xCC410000 | -50,593,792 | 부호 비트가 1이 되고 지수가 25로 커짐 |
| DCBA | 0x0000CC41 | 약 7.3272E-41 | 상위 워드가 0x0000이 되어 비정규수 |
BADC 결과를 직접 계산해 보면 원리가 보입니다. 0xCC410000에서 부호는 1, 지수 비트 1001 1000은 152이므로 152 - 127 = 25이고, 가수는 1 + 65/128 = 1.5078125입니다. 따라서 -1.5078125 × 2^25 = -1.5078125 × 33,554,432 = -50,593,792가 됩니다. CDAB 결과 0x000041CC는 지수 비트가 0인 비정규수라서 16844 × 2^-149 ≈ 2.3603E-41로 계산됩니다.
5. Modbus float 값이 1.2E-38처럼 이상하게 나올 때 확인 방법
5-1. 이상한 값의 모양으로 원인 짐작하기
float의 크기는 앞쪽 16비트에 거의 다 들어 있습니다. 합친 32비트의 앞 16비트가 0x0000~0x00FF 범위이면 결과의 절댓값은 2^-125, 곧 약 2.35E-38보다 작아집니다. 단정밀도의 가장 작은 정규수가 약 1.1755E-38이라서 "1.2E-38 근처나 그보다 작은 값"은 상위 워드 자리에 0에 가까운 레지스터가 들어갔다는 신호로 볼 수 있습니다.
| 화면에 보이는 증상 | 먼저 의심할 원인 | 확인 방법 |
|---|---|---|
| 1.2E-38 이하, E-41·E-45처럼 0에 아주 가까운 값 | 워드 순서가 반대(CDAB↔ABCD), 또는 정수 레지스터를 float로 읽음 | 워드 순서를 바꿔 보고, 사양서에서 데이터형이 INT32인지 확인 |
| 수천만 이상 큰 값, 엉뚱한 음수 | 워드 안 바이트 순서가 반대(BADC·DCBA) | 바이트 스왑 설정을 켜거나 끄고 다시 읽기 |
| 16844처럼 소수점 없는 큰 정수 | float 레지스터를 16비트 정수로 하나만 읽음 | 데이터형을 float(32비트), 읽을 개수를 2로 설정 |
| NaN, Inf 표시 | 지수 비트가 모두 1 (0xFFFF·0x7FFF 등이 상위 워드에 옴) | 센서 단선·오류 시 장비가 내는 값인지 사양서 확인 |
| 값은 그럴듯한데 다른 채널 값과 섞임 | 시작 주소가 1칸 어긋나 다른 float의 절반끼리 합쳐짐 | 40001 표기와 0부터 세는 주소 차이 확인 |
정수를 float로 잘못 읽는 경우도 비정규수 영역에 들어갑니다. 예를 들어 UINT32 값 1000(0x000003E8)을 float로 읽으면 1000 × 2^-149 ≈ 1.4013E-42가 나옵니다. 주소가 한 칸 어긋나는 문제는 Modbus 주소 체계 글에서 40001과 0번 주소의 관계를 함께 보면 빨리 찾을 수 있습니다.
5-2. 순서대로 점검하는 방법
그림 1. 기준값과 원본 16진수를 비교해 네 가지 순서 중 맞는 것을 고르고 기록합니다.
그림 1의 점검은 다섯 단계입니다. 장비 표시창이나 기준 온도계로 지금 값을 확인하고 레지스터 2개의 16진수 원본을 받아 적은 다음, 그 원본을 네 가지 순서로 합쳐 기준값과 같은 순서를 고릅니다. 이어서 값이 바뀐 뒤 한 번 더 읽어 같은 순서로 맞는지 확인하고 설정을 기록에 남깁니다.
- 기준값 확보 : 장비 표시창, 기준 온도계 등으로 지금 값이 얼마여야 하는지 알아 둡니다.
- 원본 16진수 확인 : Modbus 점검 프로그램에서 데이터형을 16비트 HEX로 두고 레지스터 2개를 그대로 읽습니다.
- 4가지 순서로 합치기 : ABCD·CDAB·BADC·DCBA를 차례로 적용해 기준값과 맞는 순서를 찾습니다.
- 두 번째 값으로 재확인 : 값이 달라진 뒤 다시 읽어 같은 순서로 맞는지 봅니다.
- 설정 기록 : 장비 모델, 주소, 데이터형, 워드 순서를 점검표에 남깁니다.
0.0은 네 가지 순서 모두에서 0으로 읽혀 순서를 가려낼 수 없으므로 시험값으로 쓰지 않습니다. 하위 워드가 0x0000이 아닌 값으로 한 번 더 확인해 두면 ABCD와 CDAB를 착각할 일이 줄어듭니다. 통신 자체가 끊기거나 예외 코드가 돌아오는 경우는 데이터형 문제와 원인이 다르니 Modbus 통신 오류 해결 방법 글의 순서를 따릅니다.
6. 현장에서 Modbus float를 설정·관리하는 방법
- 사양서 레지스터 맵 먼저 : 데이터형(FLOAT32·INT32·INT16), 워드 순서, 단위, 배율(×10, ×100)을 한 표로 옮겨 둡니다. 제품에 따라 float 대신 25.5℃를 255로 보내는 16비트 정수 배율 방식을 고를 수 있는데 이 방식은 워드 순서 문제가 생기지 않습니다.
- 장비 기종별로 설정 고정 : 같은 회선에 제조사가 다른 장비가 섞이면 순서도 다를 수 있습니다. 수집 프로그램 전체 설정 하나로 두지 말고 장비(슬레이브 주소)마다 따로 지정합니다.
- 게이트웨이 중복 변환 주의 : RTU를 TCP로 바꾸는 게이트웨이에서 한 번, 수집 프로그램에서 또 한 번 순서를 바꾸면 원래대로 돌아가거나 BADC처럼 드문 순서가 생깁니다. 바꾸는 곳은 한 군데로 정합니다.
- 알람 전 값 범위 검사 : 측정 범위를 크게 벗어난 값(예: 온도 센서에서 1E+07 이상, 1E-30 미만)은 알람보다 "데이터 이상"으로 따로 분류하면 순서 설정 오류를 실제 이탈로 착각하지 않습니다.
- 교체 후 재확인 : 같은 모델이라도 펌웨어가 바뀌면 기본 순서가 달라질 수 있으니 교체 직후 기준값 비교를 다시 합니다.
RTU와 TCP는 데이터 부분이 같아서 워드 순서 문제는 두 방식 모두에서 같은 모양으로 나타납니다. 두 방식의 차이는 RS485 / Modbus RTU / Modbus TCP 차이 글에 비교되어 있습니다.
7. 관련 산업용어
■ 엔디안(Endianness)
여러 바이트로 된 값을 메모리나 통신에서 어느 바이트부터 놓는지 정한 순서입니다. 상위 바이트를 먼저 두면 빅엔디안, 하위 바이트를 먼저 두면 리틀엔디안입니다.
■ 워드 스왑(Word Swap)
32비트 값을 이루는 16비트 워드 두 개의 순서를 맞바꾸는 것입니다. Modbus에서는 CDAB 순서를 가리키는 말로 많이 씁니다.
■ 비정규수(Subnormal Number)
IEEE 754에서 지수 비트가 모두 0인 아주 작은 수입니다. 단정밀도에서는 절댓값이 약 1.1755E-38보다 작고, 가장 작은 양수는 2^-149(약 1.4E-45)입니다.
■ NaN(Not a Number)
지수 비트가 모두 1이고 가수가 0이 아닌 값으로 "숫자가 아님"을 뜻합니다. 일부 장비는 센서 단선이나 측정 오류를 알릴 때 이 값을 보냅니다.
8. 자주 묻는 질문
Q. Modbus float 값이 1.2E-38이나 2.3E-41로 나오는 이유는?
대부분 워드 순서가 반대이기 때문입니다. 0에 가까운 하위 워드가 상위 자리로 가면 지수 비트가 0이 되어 비정규수로 읽힙니다.
Q. Modbus 규격에 float 워드 순서가 정해져 있나요?
아니요. 규격은 레지스터 하나 안의 바이트 순서(빅엔디안)만 정하고 레지스터 두 개를 합치는 순서는 장비 제조사가 정합니다.
Q. CDAB와 리틀엔디안은 같은 말인가요?
엄밀히는 다릅니다. CDAB는 워드만 바꾼 순서이고 완전한 리틀엔디안은 DCBA인데 일부 프로그램은 CDAB를 "리틀엔디안"이라고 표시하므로 바이트 문자 표기로 확인하는 편이 안전합니다.
Q. 25.5를 Modbus float로 보내면 16진수로 얼마인가요?
0x41CC0000입니다. ABCD 순서라면 레지스터 1이 0x41CC, 레지스터 2가 0x0000이고 CDAB 순서라면 두 레지스터가 서로 바뀝니다.
Q. 워드 순서를 확인할 때 0으로 시험해도 되나요?
안 됩니다. 0.0은 네 바이트가 모두 0x00이라 어떤 순서로 합쳐도 0이 나오므로 하위 워드가 0이 아닌 값으로 확인합니다.
Q. 워드 순서는 장비에서 바꿔야 하나요, 프로그램에서 바꿔야 하나요?
보통은 수집 프로그램이나 게이트웨이 쪽에서 맞춥니다. 장비 설정에서 순서를 바꿀 수 있는 제품도 있지만 여러 곳에서 동시에 바꾸면 다시 틀어지므로 한 곳에서만 바꿉니다.
9. 마무리
Modbus float 값이 이상하게 나와도 통신은 대개 정상이고 레지스터 2개를 합치는 순서와 데이터형 설정이 장비와 다를 뿐입니다. 0에 가까운 값(1.2E-38 이하)은 워드 순서, 엉뚱하게 큰 값이나 음수는 바이트 순서를 먼저 의심합니다. 원본 16진수를 받아 네 가지 순서로 합쳐 보고 기준값과 비교하면 원인을 가려낼 수 있습니다. 확인한 순서는 장비별로 기록해 두고 바꾸는 곳은 한 군데로 정합니다.
참고 기준 : Modbus Organization, MODBUS Application Protocol Specification V1.1b3 · IEEE 754-2019 Standard for Floating-Point Arithmetic
태그 : Modbus, ModbusFloat, 32비트float, IEEE754, 워드스왑, 바이트스왑, 빅엔디안, 리틀엔디안, 엔디안, CDAB, 홀딩레지스터, ModbusRTU, ModbusTCP, 부동소수점, 비정규수, 레지스터순서
- 이전글샘플링 주기와 앨리어싱, 나이퀴스트 정리로 측정 주기 정하는 법 26.11.02
- 다음글반도체 특수가스 감지, 실란·포스핀·아르신 모니터링과 추출식 샘플링 26.11.02