Modbus Decoding Modbus
Modbus Basics covered getting a
response back at all — what device to ask, what function code to use, what
an exception means. This page is the companion: what to do with the
sixteen bits when they arrive. Modbus is famous (notorious) for handing back
a number that's somehow wrong even after the read succeeded — 40001
isn't where the vendor docs said it would be, a temperature reads −12
instead of 533, a 32-bit float comes back gibberish. None of those
are protocol bugs; they're interpretation choices the protocol leaves to you.
This page walks through the four common ones and how they go sideways.
The 5-digit numbering trap
Open a vendor's Modbus integration table and you'll see addresses like
40001, 30005, 10012, 00007.
Plug 40001 into your tool, hit read, get an exception. The
off-by-one is real, and it's not a bug. It's a documentation convention older
than most people debugging it.
The leading digit names which data table the address lives in —
0 for coils, 1 for discrete inputs, 3
for input registers, 4 for holding registers. It is not part of
the wire address. The remaining four digits are the 1-based index
inside that table. So 40001 means the first holding
register — which on the wire is holding-register address 0.
40123 means the 123rd holding register — wire address
122. The off-by-one comes from the 1-based numbering meeting the
wire's 0-based addressing.
Two related conventions to know about:
- Some vendors drop the prefix and the +1 entirely — a "Modicon-style"
address table lists wire addresses straight:
0,1,2. The function code names the table; no leading digit needed. The same physical register may appear as40001in one vendor's manual and0in another's. - When the register count exceeds 9999, some vendors switch to a 6-digit
"extended" form (
400001for the first holding register). Same convention, one more digit of headroom.
Rule of thumb on the bench: when a vendor's address starts with
4 or 3 and has four digits after it, it's almost
certainly 5-digit Modbus numbering. The wire address is the documented
number minus one, with the leading digit pointing you at FC03/FC16 (holdings)
or FC04 (inputs).
Signed vs unsigned interpretation
The right register answers, the response carries sixteen bits, and now you
have to choose what those bits mean. The protocol stores them; it
doesn't tell you whether to read them as a positive count from 0
to 65535 or a signed count from −32768 to
+32767. Same bits, different intent.
Two's complement is the rule the signed interpretation follows: if the
highest bit (the "sign bit") is 0, the number is what the bit
pattern says. If the sign bit is 1, the number is the bit pattern
minus 65536. So 0xFFF3 (the example above) is
either 65523 read unsigned, or 65523 − 65536 = −13
read signed.
The clue lives in the vendor docs: INT16 means signed,
UINT16 means unsigned. When the docs don't say, the values in
use tell the story. The signature of a negative-as-unsigned misread is
values jumping near 65535 — a sensor reading
65521 isn't measuring 65521 of anything, it's a signed
−15 being read with the wrong type. Same trap on a smaller
scale with offset values that swing around zero.
Signed values show up in building controls anywhere a reading can go negative: outdoor-air temperatures, signed deltas, offset values, anything referenced to a baseline. Unsigned values are typical for counters, runtimes, percentages, setpoints, and packed bit-flag words.
32-bit values and the four byte orders
A 16-bit register can hold a temperature with a tenth of a degree of resolution, a 0–100 % setpoint, or a small counter. It cannot hold a 32-bit floating-point number, a 32-bit signed integer, or a four-byte timestamp. When the vendor wants to communicate one of those, they map it across two consecutive 16-bit registers — and there is no 32-bit- anything in the Modbus data model to enforce a byte order. So four orderings exist in the wild, all of them in current shipments, none of them the "standard."
Pick a four-byte value with bytes labeled A B C D in
transmission order — 0x12 0x34 0x56 0x78 for a worked example.
Each register holds two of those bytes. The four orderings are the four ways
a device may have laid them into the two registers:
ABCD · strict-Modbus
Big-endian word, big-endian byte. Bytes lie in the two registers in their natural transmission order. The closest thing to a Modbus "standard"; common on modern IP-side gear and platform controllers.
CDAB · Modicon word-swap
Little-endian word, big-endian byte. Each register's bytes are in network order, but the two registers are swapped. Legacy Modicon PLCs exposed an internal little-endian word layout to the wire this way.
BADC · byte-in-word swap
Big-endian word, little-endian byte. Word order preserved, byte order within each word reversed. Less common; turns up on some specialty meters and older industrial gear.
DCBA · full reverse
Little-endian word, little-endian byte. The whole 32-bit value is byte-reversed. Common on x86-derived devices that store the value in CPU-native form and forward it raw.
How to find which one a device uses: pick a register pair where you know the expected value — a configured setpoint, a calibration constant, a baseline reading. Read the two registers. The four reconstructions produce four wildly different decimal values; one of them matches the expected, and that's the device's ordering. The Modbus Register Viewer tool's 32-bit Pair tab shows all four side-by-side for exactly this exercise — you don't have to compute them by hand.
Once you've identified the ordering for a device, it holds across every 32-bit value on that device. Manufacturers don't mix orderings within a product. So this is figure-out-once, document-it-on-the-job work, not a per-register decision.
Scaling and engineering units
The last piece — and the one Modbus is most famously dumb about — is the jump from the integer stored in the register to a value with units. A holding register holds a number; the protocol doesn't know whether that number is in tenths of a degree, in percent times 10, in raw ADC counts, or in something else entirely. The vendor decides; the vendor's docs are the only thing that knows.
The patterns to recognize:
- Implicit decimal point. The most common pattern. The
vendor stores the value × 10 (or × 100) to preserve one or two decimal
places in an integer register. A reading of
523with the docs noting scale 0.1, units °F means 52.3 °F. A reading of1024with scale 0.01 means 10.24. - Offset + scale. Less common, but real. The stored
value is
(actual − offset) × scale. The docs name both numbers; both have to be applied. Older equipment that exposed calibration values directly tends to use this. - Native unit. Some values are stored directly in the units the docs claim. An event counter doesn't need scaling; an integer setpoint in percent often doesn't either.
- Packed multi-field encoding. A status word may pack several flags into one register's bits, with the docs naming what each bit means. The Modbus Register Viewer tool's binary readout is built for these.
Scaling is where the "vendor docs are load-bearing" framing from
Modbus Basics gets concrete.
A raw register without its scale tag is meaningless — and a register read
with the wrong scale tag is worse than meaningless. The classic
failure is treating an unsigned register that's actually signed-and-scaled.
A temperature reads 6553.5 (a 0.1-scaled raw
65535) when it's actually −0.1 (raw
−1 read signed, scaled by 0.1). The wrong number looks
plausible enough to log without flagging; the real value is a sensor at
the bottom of its range. Both interpretation choices have to be right.
What this page didn't cover
Two questions stay out of scope, both deliberately:
- Modbus RTU on the wire — RS-485 multidrop wiring, baud rates, parity, the two-byte CRC computation, the inter-character framing timeout. Most BMS techs touch this layer only at commissioning; a third Modbus page may land here if demand surfaces.
- The integration story — how a Niagara gateway or an EBO field controller maps Modbus points into the BAS object space, how poll rates are set, how stale-data flags propagate. Platform-specific work that belongs on the eventual Niagara / EBO pages, not here.
Between the two pages: a device address, the right function code, the right register, the right sign interpretation, the right byte order, and the right scale to engineering units. That's the BMS integration loop, end to end — enough to read a vendor's Modbus table, write the config, and trust the number on the graphic. The practitioner-grade interactive surface (signed/unsigned toggle, the four-order viewer, decimal/hex/binary readouts) lives on the Modbus Register Viewer tool, one click away.