Modbus Byte Order and Word Order: Fix Your 32-Bit Values

By | July 3, 2026

You read a float from a Modbus device. The value should be 25.3 degrees Celsius. You get 1.3 × 10³⁸. Or maybe -8.5 × 10⁻⁴¹. Or something that swings wildly between reasonable and absurd every time you re-read.

Welcome to Modbus byte and word order. It is the single most common headache when you connect a new device. And once you know the four possible arrangements and how to spot them, you fix it in about five minutes.

This guide is a decoder cookbook. Symptoms first, then the fix, then the why.

The four arrangements

A 32-bit value in Modbus lives in two consecutive 16-bit registers. Reading it from a device gives you 4 bytes. There are four ways those 4 bytes can be arranged.

Call the four bytes A, B, C, D, where A is the highest byte and D is the lowest. So the value 0x12345678 has A=0x12, B=0x34, C=0x56, D=0x78.

NameOrderAlso called
ABCDBig-endian, big-wordStandard / Modicon / IEEE format
CDABBig-endian, little-wordWord swap
BADCLittle-endian, big-wordByte swap
DCBALittle-endian, little-wordByte and word swap

The Modbus spec says each 16-bit register on the wire is big-endian (byte A comes before byte B in the first register). What the spec does not say is which register comes first for multi-register values. Every vendor picked what they wanted. That is the whole problem.

Quick decoder — symptoms to fix

If you know what value should be there, use this table to find which order the device is using.

Test: write 1.0 (float) to the device or find a register with a known 1.0 value. IEEE 754 float 1.0 = 0x3F800000.

What you read (reg1, reg2)OrderFix
0x3F80, 0x0000ABCDNo swap — read straight
0x0000, 0x3F80CDABSwap the two registers
0x803F, 0x0000BADCSwap the two bytes inside each register
0x0000, 0x803FDCBASwap bytes inside each register, then swap the registers

If you read a float and get:

SymptomLikely cause
Huge value (1e38+) or tiny (1e-38)Wrong byte or word order — bytes are shuffled
Value close to right but off by factor of ~65536Word swap needed
Negative when should be positive (or vice versa)Byte order affecting sign bit and exponent
Value keeps changing wildly on each readNot byte order — check for register overlap or wrong register number
Value is right but off by a small factorNot byte order — check scaling factor in the device manual

The five-minute test

Best way to nail down byte order on any new device:

  1. Find a register with a known value. Firmware version, model ID, or set a specific value yourself.
  2. Read the two registers as raw hex. Get the four bytes.
  3. Compare against the expected hex value.
  4. Rearrange until they match.

Example with a temperature register that should read 25.5 °C:

25.5 as IEEE 754 float = 0x41CC0000

You read: reg1 = 0x0000, reg2 = 0x41CC
Compare:  expected AB=0x41, CD=0x00

Your reg1 (0x00, 0x00) = bytes C, D  
Your reg2 (0x41, 0xCC) = bytes A, B

So the device is sending: C D A B → CDAB order → word swap

Fix: swap the two registers before decoding the float.

Fixing it in Python

Every Modbus library either has a setting for byte order or exposes raw register bytes. Here is how the four orders look in Python using struct:

python

import struct

# You read two 16-bit registers from Modbus:
reg1 = 0x0000
reg2 = 0x41CC

# ABCD (standard):
value = struct.unpack('>f', struct.pack('>HH', reg1, reg2))[0]

# CDAB (word swap — most common alternative):
value = struct.unpack('>f', struct.pack('>HH', reg2, reg1))[0]

# BADC (byte swap within each register):
def swap_bytes(word):
    return ((word << 8) | (word >> 8)) & 0xFFFF
value = struct.unpack('>f', struct.pack('>HH', swap_bytes(reg1), swap_bytes(reg2)))[0]

# DCBA (both swaps):
value = struct.unpack('>f', struct.pack('>HH', swap_bytes(reg2), swap_bytes(reg1)))[0]

If you are using pymodbus, the BinaryPayloadDecoder accepts byteorder and wordorder parameters. Set them to Big or Little to match your device.

Fixing it in your PLC code

If you are reading Modbus in a PLC (Siemens S7, ControlLogix, CODESYS), the Modbus function block usually has a byte order or word order parameter. Set it once, forget it.

  • TIA Portal / Siemens: Modbus_Master block has a MB_SWAP parameter for byte swap.
  • Studio 5000 / Rockwell: MSG instruction has a swap option in the configuration tab.
  • CODESYS: MODBUS_TCP library has bByteOrder and bWordOrder config bits.

If the block has no swap option, use a helper function. Most PLC vendors publish one in their example libraries.

Vendor defaults you will actually see

There is no universal rule. But here are the patterns most engineers hit:

Device familyCommon default
Schneider Modicon (original spec)ABCD (big-endian, big-word)
Modern Schneider PLCsABCD, configurable
Siemens gatewaysABCD, configurable per tag
Allen-Bradley via Modbus gatewayConfigurable — usually needs swap
ABB drivesBADC or CDAB common
Yaskawa drivesCDAB common
Emerson controllersVaries by product line
Power meters (PM8000, ION series)ABCD, but documented
Energy meters (generic)All four orders exist in the wild
Chinese-made devicesAll four orders exist in the wild

The pattern: Modicon-style ABCD is the closest thing to a standard. Devices from vendors close to the Modicon lineage (Schneider, most European PLCs) tend to use ABCD. Devices adapted from other native protocols (drive controllers, meters designed around other formats) tend to use CDAB or BADC because their internal representation is little-endian.

Always check the device manual. If the manual is silent on byte order, assume you will have to test.

Why does this exist

Two things collide:

  1. CPU byte order. x86 and ARM chips are little-endian internally. Older Motorola chips (68k, PowerPC) were big-endian. When a device sends a value over Modbus, the firmware often just writes memory bytes to the wire — so the byte order on the wire reflects the CPU it runs on.
  2. The Modbus spec ambiguity. The spec says each register is transmitted MSB-first (big-endian). It does not say which register comes first for a two-register value. Firmware developers picked whatever matched their internal memory layout.

Result: two dimensions of variation.

  • Byte order inside each register: big-endian (spec) or little-endian (spec violation but common).
  • Word order across registers: high word first (natural) or low word first (little-endian CPU habit).

Two options × two dimensions = four combinations = four possible byte arrangements.

Byte order vs word order — the two dimensions

Some engineers confuse “byte swap” and “word swap.” They are different problems.

Byte swap (inside a register): The 16-bit register value has its two bytes reversed. Register value should be 0x3F80 but arrives as 0x803F. This is a spec violation but shows up in devices that dumped little-endian memory to the wire.

Word swap (across registers): The two 16-bit registers are in reverse order. The high word 0x3F80 is in register 2 instead of register 1. This is not a spec violation — the spec just does not say.

Some devices do both (DCBA). Some do neither (ABCD). Some do one or the other.

You can have byte swap without word swap. You can have word swap without byte swap. You can have both. You can have neither. All four combinations exist in real production devices.

Which values are affected

Byte and word order matters for values that span multiple bytes:

Data typeBytesAffected by byte order?Affected by word order?
Boolean (coil)1 bitNoNo
INT16 / UINT16 (single register)2YesNo — single register
INT32 / UINT32 (two registers)4YesYes
REAL / Float (two registers)4YesYes
INT64 / UINT64 (four registers)8YesYes (and gets even weirder)
DOUBLE / Float64 (four registers)8YesYes
String / character arrayVariesYes (each character)No

Values inside a single 16-bit register (INT16, UINT16) never have word order issues — there is only one word. But byte order still matters.

For a value spanning four registers (INT64, DOUBLE), word order can be even messier — some devices swap the two 32-bit halves, some swap adjacent pairs, some do nothing. Always test 64-bit values with a known value before trusting them.

String handling gotchas

Modbus supports strings by packing two characters per 16-bit register. The character order inside each register follows the byte order rule.

For a string “OK”:

  • ABCD byte order: register = 0x4F4B (“O” = 0x4F, “K” = 0x4B)
  • BADC byte order: register = 0x4B4F (reversed)

If your string reads as garbage on every character pair, you have a byte order problem. Fix the byte order (swap bytes in each register) and the string decodes correctly.

Common test values

If you want a set of test values that show up different arrangements clearly:

ValueIEEE 754 hexWhy it is useful
1.00x3F800000Every byte different — spot swaps instantly
-1.00xBF800000Only differs from 1.0 in one bit — sign bit visibility
0.00x00000000Cannot use — every byte is 0, cannot spot swaps
100.00x42C80000Reasonable-looking magnitude, unique bytes
3.141590x40490FD0All four bytes different — great for swap detection
Very large (1e10)0x501502F9Four unique bytes

For INT32:

  • 305419896 (0x12345678) — every byte unique, easy to spot swaps
  • 1 (0x00000001) — most bytes are 0, hard to detect
  • -1 (0xFFFFFFFF) — all bytes same, useless for detection

Documentation reads you should watch for

Device manuals describe byte order using several conventions. Some are clear, some are not.

“Modbus is big-endian” — this is only half the story. It tells you byte order inside each register but nothing about word order.

“IEEE 754 format” — this is the encoding, not the byte order. IEEE 754 defines what the bits mean; it does not say which byte goes first on the wire.

“Modicon float” — this usually means ABCD (big-endian, big-word) — the arrangement the original Modicon devices used.

“Reversed float” or “swapped float” — usually means CDAB (word swap). Sometimes means DCBA depending on the vendor.

Hex examples in the manual — the safest way. If the manual shows “Value 1.0 = registers 0x3F80 0x0000“, that tells you the exact order. If it shows “0x0000 0x3F80“, that is CDAB.

If the manual gives no hex example, contact the vendor. Do not guess.

Reading 32-bit signed integers

The same four orders apply to INT32/DINT/Long values. But integers make some issues easier to spot than floats.

Example: expected value = 1000 (0x000003E8).

Orderreg1, reg2 read
ABCD0x0000, 0x03E8 — you read a low number
CDAB0x03E8, 0x0000 — you read 65,536,000 (huge — obviously wrong)
BADC0x0000, 0xE803 — you read 59,395
DCBA0xE803, 0x0000 — you read (huge number)

If you know the value should be around 1000 and you read 65 million, you have word order wrong. Swap the registers.

For signed integers, wrong byte order can flip the sign bit. A positive value can read as very negative. That is often the first clue for INT32 misinterpretation.

Endianness in different environments

The environment you decode in matters for how you write the fix:

Python: struct.unpack('>f', ...) is big-endian, struct.unpack('<f', ...) is little-endian.

C: Use htonl/ntohl for byte-swapping 32-bit values. On big-endian systems these are no-ops.

Node.js: Buffer.readFloatBE(0) vs Buffer.readFloatLE(0).

IEC 61131-3 (PLC): Structured Text usually has MOVE operations for byte-level manipulation. Some Modbus function blocks have byte-order parameters — use those if available.

.NET / C#: BitConverter.IsLittleEndian tells you the runtime’s native order. BinaryPrimitives.ReadSingleBigEndian() reads a big-endian float regardless.

Java: ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat().

The key insight: your local runtime’s byte order is separate from the Modbus wire byte order. Do not assume your CPU’s little-endian nature means Modbus is little-endian.

When you have to support multiple devices

Real installations often mix vendors. One PLC might read a Schneider drive (ABCD) and a legacy ABB meter (CDAB) on the same network.

Best practice: configure byte order per device, not globally. Most SCADA and PLC platforms let you set byte order at the device driver level. Do not try to normalize everything to one format at the code level — you will regret it.

If your platform does not support per-device byte order:

  1. Read raw registers into a wrapper library.
  2. Apply the correct swap per device before exposing the value to your logic.
  3. Document which device uses which order in your commissioning notes.

Common questions

What is Modbus byte order?

Modbus byte order is how the bytes of a value are arranged inside a 16-bit register. The Modbus spec says each register is transmitted big-endian — the most significant byte first. But not every device follows this. Some devices send the least significant byte first (little-endian), especially devices adapted from little-endian CPU platforms. When bytes arrive in the wrong order, your values read as garbage.

What is Modbus word order?

Modbus word order is which 16-bit register comes first when a 32-bit value spans two registers. For example, the float 1.0 (0x3F800000) has a high word (0x3F80) and a low word (0x0000). Some devices send the high word first (natural), some send the low word first (“word swap” or CDAB). The Modbus spec does not define this, so both are common.

What is the difference between byte swap and word swap?

Byte swap reverses the two bytes inside a 16-bit register: 0x3F80 becomes 0x803F. Word swap reverses the two 16-bit registers for a 32-bit value: register 1 and register 2 are swapped. They are independent problems. A device can have byte swap only, word swap only, both, or neither.

What does ABCD, CDAB, BADC, DCBA mean in Modbus?

These are shorthand names for the four possible arrangements of a 32-bit value across two Modbus registers. ABCD is standard big-endian, big-word. CDAB is word swap. BADC is byte swap within each register. DCBA is both swaps. You will see these names in most Modbus configuration tools and documentation.

How do I detect Modbus byte order?

Write a known value to the device or find a register with a known value (firmware version, model number). Read the raw registers as hex. Compare to what the value should look like in each of the four possible arrangements. The one that matches is the device’s byte order. IEEE 754 float 1.0 (0x3F800000) is a great test value because every byte is different.

Why does my Modbus float read as a huge number?

Almost always a byte or word order problem. IEEE 754 floats are very sensitive to byte arrangement — the exponent field lives in the top byte, so if you get bytes in the wrong order, the exponent goes crazy and the value blows up to 10³⁸ range. Or the value collapses to near zero. Try each of the four byte/word arrangements to find the one that matches.

Is Modbus big-endian or little-endian?

Modbus is big-endian for byte order inside each 16-bit register — the specification requires it. But word order for multi-register values is not specified, so both big-word and little-word arrangements exist in the field. So calling Modbus “big-endian” is only half the story.

What byte order do Schneider devices use?

Schneider Modicon devices (the originators of Modbus) typically use ABCD — big-endian byte order, big-endian word order. This is the closest thing to a Modbus “standard.” Other vendors may match or may not — check each device’s manual.

What byte order do Chinese-made Modbus devices use?

There is no pattern. Chinese-made Modbus devices exist in every possible byte order combination. Always test byte order when integrating a new Chinese-made device — do not assume it follows any standard.

How do I fix Modbus byte order in Python?

Use the struct module. To read two registers as a big-endian float: struct.unpack('>f', struct.pack('>HH', reg1, reg2))[0]. For word swap (CDAB), swap reg1 and reg2 in the pack call. For byte swap (BADC), swap the two bytes within each register value before packing. The pymodbus library also has a BinaryPayloadDecoder with byteorder and wordorder parameters.

How do I fix Modbus byte order in a PLC?

Most PLC platforms (Siemens TIA Portal, Rockwell Studio 5000, CODESYS) include byte-order parameters in their Modbus function blocks. Look for parameters named MB_SWAP, byte order, word order, or similar. Setting these correctly at the block level fixes the interpretation without any code changes. If your platform lacks these parameters, write a small helper function that rearranges bytes before your logic uses the value.

Do all data types have byte order issues?

Only values that span multiple bytes. INT16/UINT16 (single register) have byte order issues but no word order issues. INT32/UINT32/REAL/Float (two registers) have both. INT64/UINT64/DOUBLE (four registers) have both, plus potential issues with which pair of registers comes first. Booleans (coils/discrete inputs) are single bits and are not affected. Strings are affected by byte order (character pairs may swap).

Should I normalize all byte orders in my code?

Not at the application logic layer. Best practice is to configure byte order per device at the driver or Modbus reader level. This keeps your logic code clean and lets you document per-device byte order in one place. Trying to normalize inside application code makes debugging harder and multiplies work when devices change.

What if my device manual does not specify byte order?

Contact the vendor. If the vendor cannot answer, run a known-value test with the device — write a specific value, read the raw bytes, and reverse-engineer the byte order. Document what you find in your commissioning notes so the next engineer does not have to redo the work.

Author: Zakaria El Intissar

I've spent 13 years in power system automation, electrical protection, and SCADA communication, as an automation and industrial computing engineer. ScadaProtocols.com is where I turn what I've learned on site into plain guides and working tools — so other engineers can decode, analyze, and troubleshoot industrial communication protocols without the guesswork.