📝 Protocol

Reading a Modbus Register Map: Data Types, Byte Order and Address Offsets

Argus EMS · 21.09.2026 · 6 min read

Reading a Modbus Register Map: Data Types, Byte Order and Address Offsets — Argus EMS

The power meter is mounted, the link is up and every poll gets a reply, yet the dashboard shows 2.3×10²³ V instead of 230 V, the frequency sits at zero and the energy counter either freezes or jumps around. Crews often blame the cabling. But if the device answers, the physical layer is almost certainly fine; what has gone wrong is how the register map was interpreted. This article skips protocol basics and focuses on the four things in a map that decide whether a value is right: the data table and function code, the address base, the data type, and the byte and word order.

What a Register Map Actually Tells You

Each row in a vendor document describes one parameter: its name, address, table or function code, data type, register count, scale factor, engineering unit and access rights. Modbus defines four data tables, and each has its own read function:

TableClassic notationRead functionContent
Coil0xxxxFC01Single bit, read and write
Discrete input1xxxxFC02Single bit, read only
Input register3xxxxFC0416-bit word, read only
Holding register4xxxxFC0316-bit word, read and write

The protocol only moves 16-bit words. A 32-bit power reading or an IEEE 754 float spans two registers, and a 64-bit energy counter spans four. Querying the wrong table, holding instead of input, either returns an ILLEGAL DATA ADDRESS exception or, worse, silently returns a different parameter that happens to live at the same number.

The Address Offset Trap: 40001 or 0?

Documentation writes addresses in three styles: classic notation (40001), a one-based register number (Register 1) and a zero-based protocol address (0x0000). On the wire, only the zero-based offset exists: 40001 means FC03 at address 0, and 40108 means address 107. Many software libraries are zero-based, while some SCADA tools expect the one-based number and subtract one internally. Subtract twice, or not at all, and every read lands exactly one register away.

An off-by-one error rarely shows up as a fault, because the device happily returns the neighbouring register. Single-register values turn into the adjacent parameter, while 32-bit values glue the low word of one parameter to the high word of the next and produce nonsense. We ran into this on site with a chiller whose building automation document listed one-based register numbers while the reader expected zero-based addresses; leaving water temperature only matched the controller display once the address was set to the register number minus one. Another manufacturer's document already used zero-based numbers, so subtracting one there would have caused the very same shift. The lesson is simple: never assume the convention, verify it for every device model.

Data Types and Scaling

Data typeRegistersTypical useWatch out for
UINT161Frequency, status codes0xFFFF may mean no data
INT161Power factor, temperatureNegative values use two's complement
UINT32 / INT322Voltage, current, active powerWord order and sign bit
FLOAT322Unscaled physical valuesByte and word order are critical
UINT64 / INT644Energy countersWord order varies by vendor
ASCIINSerial number, model nameTwo characters per register

Mixing signed and unsigned types is a quiet source of trouble. A value sent as INT16 −15 reads as 65521 when decoded as UINT16. On the consumption side, negative power is already a red flag for a wiring fault such as reversed current transformer polarity; choosing the wrong type hides that warning behind a large positive number. For integer registers the real value is raw value × multiplier + offset: a tenth for voltage and a thousandth for power factor are common, and devices reporting Fahrenheit need both a multiplier and an offset.

Then there are invalid-value markers. Plenty of devices flag missing data with the extreme values of a type: 0xFFFF, 0x7FFF, 0x8000, or 0x80000000 for 32-bit fields. We have seen a generator controller send 0xFFFF7FFF for an invalid reading. Store those as numbers and your trend charts fill up with giant spikes.

Byte and Word Order: ABCD, CDAB, BADC, DCBA

The Modbus specification puts the high byte first inside a single register, but it says nothing about the order of words in a multi-register value. That is why four layouts appear in the field. Label the bytes of a 32-bit value A, B, C and D from most to least significant:

  • ABCD (big-endian): high word first; the most common layout.
  • CDAB (word swap): low word first with bytes intact inside each word; frequent on PLCs and some meters.
  • BADC (byte swap): words in the right order, bytes swapped within each word.
  • DCBA (little-endian): fully reversed.

Take a FLOAT32 carrying 230.5 V, which is 0x43668000. If the device uses one of the four layouts and the software assumes ABCD, this is what comes out:

Device layoutRegisters receivedDecoded assuming ABCD
ABCD0x4366 0x8000230.5
CDAB0x8000 0x4366−2.4×10⁻⁴¹
BADC0x6643 0x00802.3×10²³
DCBA0x0080 0x66431.2×10⁻³⁸

With floats, the wrong layout almost always gives an absurd result, so it gets caught quickly. Integer counters are sneakier: a word-swapped energy register shows a plausible but wrong number that leaps whenever the low word rolls over. Sixty-four-bit counters have no consensus either; one brand sends its four words high to low while another on the same site sends the low word first.

A Step-by-Step Verification Routine

  1. Identify how the document writes addresses: classic 4xxxx, one-based numbers or zero-based hex.
  2. Pick a slowly changing reference visible on the device display, such as grid frequency or a fixed setpoint.
  3. Read the raw registers in hex with the correct function code, including the address just before and just after the target.
  4. Try all four byte layouts for the data type and keep the physically sensible one.
  5. Check the sign on a signed parameter such as power factor.
  6. Apply multiplier and offset, compare with the display, and repeat under different load conditions.
  7. Write down the device's invalid-value markers.
  8. Avoid block reads that span undefined addresses, since some devices reject the whole block. A single read is limited to 125 registers.

How Argus EMS Stores Register Maps

In Argus EMS every device model is described by a template. Each register row holds the start address, register count, data type (INT16, UINT16, INT32, UINT32, FLOAT32 and others), function code (holding or input), byte order, multiplier, offset, unit, and whether the value is instantaneous or cumulative. A verified map is written once and reused for every device of that model. The field agent readers on the site PC work with zero-based protocol addresses, decode 16, 32 and 64-bit values by type, and leave known invalid-value markers empty instead of turning them into numbers.

One feature that pays off during commissioning: a remote management command can read a device's raw registers with FC03 or FC04 and return them as decimal and hex dumps, so address and byte-order trials happen without a site visit. Readings collected over Modbus TCP and RTU are forwarded to the server via MQTT. If the connection drops, data is buffered in a local SQLite queue for up to 30 days and is never deleted before the server acknowledges it, while the server keeps raw readings for 13 months.

Related Content

What Is Modbus TCP? Energy MonitoringModbus TCP Energy Monitoring & IntegrationIntegrations — Modbus, M-Bus, MQTTenergy tracking and management systemreactive penalty preventiontransformer load and temperature monitoringWhat Is an Energy Analyzer? Buyer's GuideWhat Is M-Bus? Wired & Wireless M-Bus

FAQ

The manual says 40001. Which address do I enter in my software?
In classic notation the leading 4 identifies the holding register table and the rest is a one-based register number. On the wire this becomes FC03 at address 0. Check whether your software expects the one-based number or the zero-based offset, then confirm the result against a parameter you can read on the device display.
How can I tell that the byte order is wrong?
Float values turn into absurdly tiny or huge numbers. Integer counters may look plausible but jump in large steps whenever the low word rolls over. Read the raw registers in hex, try the ABCD, CDAB, BADC and DCBA layouts one by one, and keep the one that matches the display.
Why do some parameters keep reading 65535 or -32768?
These are usually the extreme values a device uses to signal missing or invalid data. Another possibility is a signed value being decoded as unsigned. Look for the invalid-value definition in the manual, double-check the data type, and never plot those markers as real measurements.

Monitor Your Facility with Argus

See Argus at Your Facility

Explore the system with your own data in a demo session.