Modbus Data Formats: Bits, Bytes, and Byte Ordering
Imagine walking into a massive library with thousands of books. Each book has a unique call number, each page has a page number, and each line on the page contains specific information. Now imagine trying to find a specific sentence—you need to know the exact book, page, and line number.
This library analogy perfectly describes Modbus data formats. Modbus devices store data in a structured way, and understanding this structure is crucial for accurate communication. Let’s demystify the common confusion points around Modbus data representation.
The Library Analogy: Modbus Data Organization
| Library Component | Modbus Component | Example |
|———————–|———————-|————-|
| Library Building | Modbus Network | A factory floor network |
| Book | Modbus Device | A temperature controller |
| Book Call Number | Device Address | Slave ID 5 |
| Page | Register | Holding Register 40001 |
| Line on Page | Bit | Coil 00001 (on/off state) |
| Sentence | Data Value | Temperature reading of 25.0°C |
| Page Format | Data Format | 16-bit integer, 32-bit float, etc. |
Zero-Based vs One-Based Addressing: The Great Confusion
This is the single most confusing aspect of Modbus for beginners. Modbus uses two different addressing schemes:
1. One-Based Addressing (Documentation Format)
- Analogy: Library books numbered from 1 upwards
2. Zero-Based Addressing (Programming Format)
- Analogy: Computer memory addressing (starts at 0x0000)
Conversion Formula
| Data Type | One-Based Address | Zero-Based Address | Hex Address |
|—————|———————–|————————|—————–|
| Coils | 00001 | 0 | 0x0000 |
| Discrete Inputs | 10001 | 0 | 0x0000 |
| Input Registers | 30001 | 0 | 0x0000 |
| Holding Registers | 40001 | 0 | 0x0000 |
Example Conversion
If a device manual says “Temperature is stored in register 40005”:
- Actual Modbus message: Will use address 0x0004
Bits and Bytes: The Building Blocks
Basic Definitions
- Quad Word: 64 bits (0x0000000000000000 to 0xFFFFFFFFFFFFFFFF)
Modbus Register Structure
- Bit Numbering: Bits are numbered from 0 (LSB) to 15 (MSB)
Example 16-bit Register:
“`
Bit Position: 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0
Byte: HIGH BYTE | LOW BYTE
Value: 0 1 0 1 0 1 0 1 | 0 1 1 0 1 0 1 0
Hex: 0x55 | 0x6A
Full Register: 0x556A
Decimal: 21866
“`
Big-Endian vs Little-Endian: Byte Order Matters
This is another major source of confusion, especially when dealing with multi-register values (32-bit integers, floats, etc.).
What is Endianness?
Endianness refers to the order in which bytes are stored in memory.
1. Big-Endian (Modbus Default)
- Example: Number 0x12345678 stored in two registers:
– Register 1: 0x1234 (MSB first)
– Register 2: 0x5678 (LSB last)
2. Little-Endian (Non-Standard Modbus)
- Example: Number 0x12345678 stored in two registers:
– Register 1: 0x5678 (LSB first)
– Register 2: 0x1234 (MSB last)
Visual Representation
“`
Value to store: 0x12345678 (decimal: 305419896)
BIG-ENDIAN (MODBUS STANDARD):
┌─────────────────┐ ┌─────────────────┐
│ Register 40001 │ │ Register 40002 │
│ 0x12 0x34 │ │ 0x56 0x78 │
└─────────────────┘ └─────────────────┘
MSB First LSB Last
LITTLE-ENDIAN (NON-STANDARD):
┌─────────────────┐ ┌─────────────────┐
│ Register 40001 │ │ Register 40002 │
│ 0x78 0x56 │ │ 0x34 0x12 │
└─────────────────┘ └─────────────────┘
LSB First MSB Last
“`
16-bit Register Limitations: Working Beyond Native Size
Modbus natively supports 16-bit registers, which limits the range of values that can be stored in a single register:
| Data Type | Size (bits) | Range | Register Count |
|—————|—————–|———–|——————-|
| INT16 | 16 | -32,768 to 32,767 | 1 |
| UINT16 | 16 | 0 to 65,535 | 1 |
| INT32 | 32 | -2,147,483,648 to 2,147,483,647 | 2 |
| UINT32 | 32 | 0 to 4,294,967,295 | 2 |
| FLOAT32 | 32 | ±1.175494351e-38 to ±3.402823466e+38 | 2 |
| INT64 | 64 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 4 |
| FLOAT64 | 64 | ±2.2250738585072014e-308 to ±1.7976931348623157e+308 | 4 |
Why This Matters
When dealing with large values (like high-resolution measurements, timestamps, or large counts), you need to combine multiple 16-bit registers. This is where endianness becomes critical!
Data Type Conversion Examples
Let’s walk through step-by-step examples of converting Modbus register values to actual data types.
Example 1: INT32 (Signed 32-bit Integer)
Scenario: A flow meter stores total flow in registers 40001-40002 as a 32-bit signed integer, big-endian format.
Register Values:
- Register 40002: 0x86 0xA0 (Hex: 0x86A0, Decimal: 34464)
Conversion Steps:
1. Combine registers in big-endian order: 0x000186A0
2. Convert to decimal: 0x000186A0 = 99904
3. Since it’s signed, check if the highest bit is set (it’s not, so positive)
4. Final value: 99,904
Example 2: FLOAT32 (32-bit Floating Point)
Scenario: A temperature sensor stores temperature in registers 40001-40002 as IEEE 754 32-bit float, big-endian.
Register Values:
- Register 40002: 0x00 0x00 (Hex: 0x0000, Decimal: 0)
Conversion Steps:
1. Combine registers in big-endian order: 0x41C80000
2. Break down IEEE 754 format:
– Sign bit: 0 (positive)
– Exponent: 0x1C8 (binary 111001000) → 127 – 127 = 0 (bias is 127 for 32-bit float)
– Mantissa: 0x000000 → 0.0
3. Calculate value: (-1)^0 × 2^(124-127) × (1 + 0.0) = 1 × 2^-3 × 1 = 0.125
4. Wait, that’s wrong! Wait, no—wait the actual breakdown is:
Wait, let’s do this correctly. IEEE 754 32-bit float has:
– 1 bit sign, 8 bits exponent, 23 bits mantissa
– 0x41C80000 in binary: 0 10000011 10010000000000000000000
– Sign: 0 (positive)
– Exponent: 10000011 = 131 → 131 – 127 = 4
– Mantissa: 10010000000000000000000 → 1.10010000000000000000000 (add implicit leading 1)
– Value: 1.1001 × 2^4 = 18.0
5. Final temperature: 18.0°C
Example 3: UINT32 (Unsigned 32-bit Integer) with Little-Endian
Scenario: A counter stores total events in registers 40001-40002 as 32-bit unsigned integer, little-endian.
Register Values:
- Register 40002: 0x01 0x02 (Hex: 0x0102, Decimal: 258)
Conversion Steps:
1. Combine registers in little-endian order: Register2 + Register1 → 0x0102ABCD
2. Convert to decimal: 0x0102ABCD = 16,909,069
3. Since it’s unsigned, no sign bit to check
4. Final value: 16,909,069 events
Practical Applications and Best Practices
When to Use Which Data Type
| Application | Recommended Data Type | Register Count |
|——————|—————————|——————-|
| Temperature | FLOAT32 or INT16 (with scaling) | 2 or 1 |
| Flow Rate | FLOAT32 | 2 |
| Pressure | FLOAT32 or INT16 | 2 or 1 |
| Counter Values | UINT32 | 2 |
| Timestamps | UINT32 or INT64 | 2 or 4 |
| Boolean Status | 1-bit Coil or Discrete Input | 1 bit |
Scaling Values
Many devices store values as scaled integers to avoid floating-point complexity:
- Always check the device manual for scaling factors!
Troubleshooting Common Data Format Issues
| Issue | Possible Cause | Solution |
|———–|——————-|————–|
| Negative values showing as large positive | Signed/unsigned mismatch | Use correct data type in your software |
| Values way too high/low | Endianness mismatch | Reverse register order |
| Decimal values missing | Scaling factor not applied | Check manual for scaling information |
| Wrong address | One-based/zero-based confusion | Convert addresses correctly |
| Float values wrong | IEEE 754 format not followed | Verify device uses standard IEEE 754 |
Conclusion: Mastering Modbus Data Formats
Understanding Modbus data formats is like learning the Dewey Decimal System for industrial communication. Once you grasp the basics of addressing, bit ordering, and data types, you can communicate effectively with any Modbus device.
The key takeaways are:
1. Addressing: Convert between one-based (documentation) and zero-based (programming) correctly
2. Endianness: Always confirm the byte order for multi-register values
3. Data Types: Know how many registers your data type requires
4. Scaling: Check device manuals for any scaling factors
5. Testing: Use Modbus testing tools to verify data interpretation
With this knowledge, you’ll be able to confidently configure, program, and troubleshoot Modbus systems, ensuring accurate data exchange between all your industrial devices.