# Module 7: Data Types

> INT16, UINT16, INT32, FLOAT, word/byte order, and the encoding traps you'll hit.

**Phase:** Addressing & Data Meaning  
**Estimated time:** 8 min  
**Release status:** Published  
**Content version:** 1.0.0  
**Source:** https://learnmodbus.studioseventeen.io/lesson/data-types

## What you'll be able to do
- Decode INT16, UINT16, INT32, UINT32, and IEEE-754 floats from raw registers.
- Identify ABCD / CDAB / BADC / DCBA word-order patterns from a capture.
- Explain why a 'valid Modbus response' can still produce the wrong number.

## Three things people get wrong
1. **Reading a 32-bit value as one register.** 32-bit values span two consecutive registers — request quantity 2, then assemble.
2. **Assuming little-endian like a CPU.** Modbus registers are big-endian (high byte first). Word order across the two registers is vendor-specific.
3. **Trusting tool labels like 'swap'.** Different tools use 'swap' to mean different things. Verify with a register whose expected value is known.

## From the field
**The float that crashed an alarm system**

A new gateway changed default word order from ABCD to CDAB during a firmware update. Suddenly tank levels read as denormal numbers near zero — and the low-level alarm fired continuously. Rolling back the firmware was faster than rewriting the alarm logic, but the post-mortem demanded an explicit word-order column on every row.

## References
- [Modbus Application Protocol V1.1b3](https://www.modbus.org/file/secure/modbusprotocolspecification.pdf) — Endianness — section 4.2
- [IEEE 754 single precision](https://en.wikipedia.org/wiki/Single-precision_floating-point_format)

## Lesson

# Module 7 Lesson: Data Types And Word Order
## Learner Outcome

By the end of this module, learners can explain what Modbus actually transports for data, then decode common vendor-defined representations: unsigned and signed integers, fixed-point values, IEEE-754 floats, packed bits, enums, BCD-style values, strings, and multi-register values. They can state signedness, width, byte order, word order, scale, and source evidence instead of hiding those assumptions.

## Key Concepts

- Modbus register data is transported as 16-bit register values. The frame does not say whether a register is signed, unsigned, scaled, a bitfield, an enum, a string fragment, or part of a larger value.
- Multi-register values are vendor or profile conventions layered on top of Modbus. A 32-bit value usually uses two consecutive registers; a 64-bit value usually uses four.
- Signedness is an interpretation choice unless the map says otherwise. The same raw word can decode differently as unsigned 16-bit and signed two's-complement.
- Byte order inside each transmitted 16-bit register value is protocol-defined in the response data. Word order across multiple registers is not universally defined by Modbus.
- A correct Modbus transaction can still produce a wrong engineering value if the client applies the wrong data type, signedness, scale, word order, or unit.

## Key Terms

- Signedness: whether a numeric value can represent negative numbers.
- Width: how many bits or registers the value uses.
- 32-bit value: a value spread across two 16-bit Modbus registers.
- Float: a floating-point value whose register order still depends on the source map or tool convention.
- Byte order: the order of bytes inside a field or register grouping.
- Word order: the order of 16-bit registers that make up a larger value.
- Known-value test: a comparison against a value whose real meaning is already known.

## Source-Grounded Explanation

At the protocol layer, a read response gives a function code, a byte count, and data bytes. The protocol can tell the client how many bytes arrived. It does not tell the client that `FF CE` means `-50`, that `00 01 86 A0` means `100000 Wh`, or that `41 48 00 00` means an IEEE-754 float value of `12.5`.

The interpretation comes from a register map, a device profile, a tool configuration, or verified field evidence.

### 16-Bit Signedness

| Raw hex | Unsigned 16-bit | Signed 16-bit two's-complement |
|---:|---:|---:|
| `0000` | `0` | `0` |
| `0001` | `1` | `1` |
| `7FFF` | `32767` | `32767` |
| `8000` | `32768` | `-32768` |
| `FFCE` | `65486` | `-50` |
| `FFFF` | `65535` | `-1` |

Field rule: decode the correct width first. A signed 32-bit value split across two registers is not the same thing as two independent signed 16-bit values.

### Multi-Register Order

Use a neutral byte label when teaching multi-register values. If the intended 32-bit byte sequence is `A B C D`, common layouts are:

| Label | Register 1 | Register 2 | Description |
|---|---|---|---|
| `ABCD` | `A B` | `C D` | high word first, high byte first inside each word |
| `CDAB` | `C D` | `A B` | low word first, high byte first inside each word |
| `BADC` | `B A` | `D C` | high word first, bytes swapped inside each word |
| `DCBA` | `D C` | `B A` | low word first, bytes swapped inside each word |

Source boundary: the official Modbus protocol defines the register data bytes in a response, but it does not assign a universal meaning to `ABCD`, `CDAB`, `BADC`, or `DCBA` for every 32-bit vendor value. Tool labels such as big-endian, little-endian, byte swap, and word swap must be checked against the specific tool and version.

## Practical Example

### Example 1: Unsigned 32-Bit Counter

Synthetic source row:

| Field | Value |
|---|---|
| Parameter | Total energy import |
| Documented address | `40031` |
| Actual PDU address | `30` |
| Function code | `03` Read Holding Registers |
| Quantity | `2` registers |
| Type | unsigned 32-bit integer |
| Word order | high word first (`ABCD`) |
| Scale/unit | raw `Wh` |
| Expected normal response | `03 04 00 01 86 A0` |

Decode:

```text
Data bytes: 00 01 86 A0
ABCD unsigned 32-bit: 0x000186A0 = 100000 Wh
CDAB unsigned 32-bit: 0x86A00001 = 2258632705 Wh
```

The first value fits a small teaching meter. The second is a possible number mathematically, but it is not plausible for this scenario unless other evidence supports it. Do not choose the order by plausibility alone; record the source row and any known-value comparison.

### Example 2: IEEE-754 Float Fixture

The Module 7 decoder fixture uses a known float value:

| Field | Value |
|---|---|
| Parameter | Lab float reference |
| Function code | `03` Read Holding Registers |
| Documented address | `40071` |
| Actual PDU address | `70` |
| Quantity | `2` registers |
| Intended type | IEEE-754 32-bit float |
| Intended order | high word first (`ABCD`) |
| Data bytes | `41 48 00 00` |
| Expected decoded value | `12.5` |

Normal response PDU:

```text
03 04 41 48 00 00

03            function code echoed
04            byte count: 4
41 48 00 00   two registers interpreted as IEEE-754 float, ABCD order
```

Common wrong-order tests:

| Assumption | Byte sequence used for decode | Result to record |
|---|---|---|
| Float, `ABCD` | `41 48 00 00` | `12.5` |
| Float, `CDAB` | `00 00 41 48` | tiny nonphysical value |
| Float, `BADC` | `48 41 00 00` | very large value |
| Float, `DCBA` | `00 00 48 41` | tiny nonphysical value |

Those wrong values do not prove the device is broken. They prove the Modbus transaction and the data interpretation are separate problems.

## Common Pitfalls

- Assuming a tool's positive decimal display means the underlying value is unsigned.
- Applying scale before deciding signedness and width.
- Treating a 32-bit or 64-bit value as independent 16-bit registers.
- Trying byte and word orders until a number looks reasonable, then failing to document the evidence.
- Reading the right address with the wrong function code and diagnosing the result as a data-type issue.
- Ignoring BCD, string padding, enum tables, local-time assumptions, and sentinel values because the first few values were plain integers.
- Treating tool UI wording as an official Modbus term.

## Instructor Flow

1. Start with one raw 16-bit word and ask learners to decode it unsigned, signed, bitfield, and enum.
2. Move to two-register values and label the bytes `A B C D`.
3. Decode one known unsigned 32-bit counter and one known IEEE-754 float using multiple order assumptions.
4. Ask learners to separate protocol fields from synthetic map meanings and tool behavior.
5. End with the rule: correct communication is necessary, but it is not sufficient for correct data.

## Check-Your-Understanding

1. A register returns `FF CE`. What are the unsigned and signed 16-bit interpretations?
2. A float value looks impossible, but the function code, PDU address, quantity, and byte count are correct. What assumptions should you test before changing the address?
3. Why does a Modbus response not tell you whether `0003` means number `3`, enum state `Running`, active bit flags, or part of a string?
4. A device map says `U32, high word first`. Is the word-order claim official protocol behavior or vendor-specific behavior?

## Source Notes

- Official rule: Modbus Application Protocol Specification V1.1b3 defines logical tables, function codes, address fields, quantity fields, response shapes, byte counts, and register data in protocol messages.
- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for multi-byte numeric encoding in protocol fields, four primary logical data tables, addressing model and zero-based PDU addresses, read holding/input register quantity limits, and normal response vs exception response.
- Vendor-specific note: signedness, width beyond 16 bits, IEEE-754 use, strings, BCD, enums, scale, units, validity, and word order come from vendor documentation, device profiles, synthetic course data, or verified behavior.
- Synthetic source: `release/synthetic-course-device-map.md` provides the current no-hardware fixtures.
- Tool source: tool-specific byte/word-order labels still need versioned verification before release.

## Completion Checkpoint

Before moving on, learners should decode the same two-register value more than one way, identify which interpretations are plausible, and explain what map, known-value, or tool evidence is still needed before calling one interpretation correct.

## Reusable Assets

- [Synthetic course device map](https://learnmodbus.studioseventeen.io/resource/synthetic-course-device-map)
- [Data type and byte-order worksheet](https://learnmodbus.studioseventeen.io/resource/data-type-byte-order-worksheet)
- [Word-order decoding lab](https://learnmodbus.studioseventeen.io/lesson/data-types?page=lab-1)
- [Module 7 quiz](https://learnmodbus.studioseventeen.io/lesson/data-types?page=quiz)
- [Normalized register map template](https://learnmodbus.studioseventeen.io/resource/normalized-register-map-template)

## Diagram source

```mermaid
flowchart LR
  R1["Reg N: 0x4248"]
  R2["Reg N+1: 0x0000"]
  R1 --> AB["ABCD<br/>(big-endian)"]
  R2 --> AB
  R1 --> CD["CDAB<br/>(word-swapped)"]
  R2 --> CD
  AB --> V1["50.0 °C"]
  CD --> V2["1.847e-41"]
```

## Labs

### Lab 1

# Lab 7: Word Order And Data Type Decoding
## Learner Outcome

Learners can decode the same Modbus register bytes under several type and order assumptions, identify the interpretation supported by the source evidence, and explain why a valid Modbus response can still produce a wrong value.

## Prerequisites

- Module 3: data tables and 16-bit registers.
- Module 4: read function response shape.
- Module 5: documented address versus PDU address.
- Module 6: normalized register-map rows.
- Module 7 lesson through multi-register order.

## Safety And Scope

This lab uses [Synthetic Course Device Map](https://learnmodbus.studioseventeen.io/resource/synthetic-course-device-map) and [Data Type And Byte-Order Worksheet](https://learnmodbus.studioseventeen.io/resource/data-type-byte-order-worksheet). It is a paper or spreadsheet exercise until the simulator fixture is implemented.

Do not use the worksheet results to configure production equipment. Real devices require their own manual, known-value test, and tool-version verification.

## Scenario

Your client successfully reads two holding registers, but the displayed value is obviously wrong. The request fields match the source row: function code, PDU address, and quantity. The response also has the expected function code and byte count. The likely problem is data interpretation.

Your job is to decode the bytes several ways, record the assumptions, and decide which interpretation is supported by the synthetic source evidence.

## Protocol Target

Fixture A: unsigned 32-bit counter from the synthetic course map.

| Field | Value |
|---|---|
| Function code | `03` Read Holding Registers |
| Documented address | `40031` |
| Actual PDU address | `30` |
| Quantity | `2` registers |
| Type from source | unsigned 32-bit |
| Word order from source | high word first (`ABCD`) |
| Normal response | `03 04 00 01 86 A0` |
| Expected decoded value | `100000 Wh` |

Fixture B: IEEE-754 float reference for Module 7.

| Field | Value |
|---|---|
| Function code | `03` Read Holding Registers |
| Documented address | `40071` |
| Actual PDU address | `70` |
| Quantity | `2` registers |
| Type from source | IEEE-754 32-bit float |
| Word order from source | high word first (`ABCD`) |
| Normal response | `03 04 41 48 00 00` |
| Expected decoded value | `12.5` |

## Procedure

1. Copy the two normal response PDUs into the worksheet.
2. Confirm that the request used function `03`, the correct PDU address, and quantity `2`, then confirm that the response echoed function `03` with byte count `04`.
3. Strip the function code and byte count so only data bytes remain.
4. Decode Fixture A as unsigned 32-bit `ABCD`, unsigned 32-bit `CDAB`, signed 32-bit `ABCD`, and IEEE-754 float `ABCD`.
5. Decode Fixture B as IEEE-754 float `ABCD`, `CDAB`, `BADC`, and `DCBA`.
6. For each decode, record whether the assumption came from an official protocol field, synthetic vendor note, tool behavior, or a test hypothesis.
7. Choose the interpretation supported by the source row and explain why the other results are wrong for the scenario.
8. Fill the Data Meaning Ledger in the worksheet for the supported interpretation.
9. List the evidence still needed before calling this runnable in a real Modbus client.

## Auto-Graded Checks

```lab
type: decode
prompt: Decode Fixture A's data bytes as U32 with high word first (ABCD).
bytes: 00 01 86 A0
as: u32
order: ABCD
unit: Wh
hint: Concatenate the bytes in ABCD order, then read as unsigned 32-bit big-endian.
explain: 0x000186A0 = 100000.
toolLink: /tools?tab=decoder&hex=000186A0
```

```lab
type: decode
prompt: Decode Fixture B's data bytes as IEEE-754 float with high word first (ABCD).
bytes: 41 48 00 00
as: f32
order: ABCD
hint: 0x41480000 in IEEE-754 single precision.
explain: 0x41480000 = 12.5.
toolLink: /tools?tab=decoder&hex=41480000
```

```lab
type: decode
prompt: Now decode the same Fixture B bytes with the wrong word order (CDAB). What value would a misconfigured client display?
bytes: 41 48 00 00
as: f32
order: CDAB
hint: Swap the two 16-bit words before reading as float.
explain: Bytes become 00 00 41 48 → an extremely small denormal float — a classic sign of inverted word order.
```

## Worksheet

| Fixture | Data Bytes | Decode Assumption | Decoded Value | Source Label | Plausible For Scenario? | Evidence Or Note |
|---|---|---|---:|---|---|---|
| A | `00 01 86 A0` | U32 `ABCD` |  |  |  |  |
| A | `00 01 86 A0` | U32 `CDAB` |  |  |  |  |
| A | `00 01 86 A0` | S32 `ABCD` |  |  |  |  |
| A | `00 01 86 A0` | float `ABCD` |  |  |  |  |
| B | `41 48 00 00` | float `ABCD` |  |  |  |  |
| B | `41 48 00 00` | float `CDAB` |  |  |  |  |
| B | `41 48 00 00` | float `BADC` |  |  |  |  |
| B | `41 48 00 00` | float `DCBA` |  |  |  |  |

## Expected Results

| Fixture | Decode Assumption | Expected Result | Interpretation |
|---|---|---:|---|
| A | U32 `ABCD` | `100000 Wh` | Correct for the synthetic source row. |
| A | U32 `CDAB` | `2258632705 Wh` | Wrong word order for the source row. |
| A | S32 `ABCD` | `100000 Wh` | Same numeric result here because the high bit is not set; still the wrong type label for the row. |
| A | float `ABCD` | tiny nonphysical value | Wrong data type for the row. |
| B | float `ABCD` | `12.5` | Correct for the Module 7 fixture. |
| B | float `CDAB` | tiny nonphysical value | Wrong word order for the fixture. |
| B | float `BADC` | very large value | Byte-swapped interpretation, not supported by the fixture. |
| B | float `DCBA` | tiny nonphysical value | Wrong byte and word order for the fixture. |

## Expected Observations

- A successful Modbus response proves the server returned bytes. It does not prove the client decoded the bytes correctly.
- `ABCD` and `CDAB` are teaching labels for byte sequence assumptions, not universal protocol settings.
- Fixture A shows why signedness can be invisible when the high bit is not set.
- Fixture B shows why float-order mistakes often look like impossible process values rather than communication failures.
- The Data Meaning Ledger should preserve the source row, raw bytes, decode settings, and unresolved uncertainty for later troubleshooting.

## Troubleshooting Notes

- If a value is wrong but the byte count and response shape are correct, check address, table, type, signedness, word order, scale, and unit as separate hypotheses.
- Do not change several assumptions at once. Record one decode attempt per row.
- Do not trust a tool's "little-endian" or "swapped" label until you know exactly how that tool applies it to Modbus registers.

## Check Questions

1. Which fixture proves communication success but data interpretation failure?
2. Why is U32 `ABCD` and S32 `ABCD` the same numeric result for Fixture A?
3. What evidence supports float `ABCD` for Fixture B?
4. What tool or simulator evidence is still needed before this lab becomes runnable rather than paper-based?

## Instructor Notes

- Keep the first pass calculator-free if possible: learners should label bytes and words before entering values in any tool.
- Reward evidence labels. The correct answer is not only the number; it is the source-backed assumption behind the number.
- If a learner changes the address to fix a float-order symptom, pause and return to the normalized row.

## Source Notes

- Official source: Modbus Application Protocol Specification V1.1b3 for read response structure, byte count, and register data fields.
- Synthetic source: `release/synthetic-course-device-map.md` for fixture meanings and expected values.
- Course-owned helper evidence: `python3 tools/modbus_fixture_helper.py decode-fixtures` verifies the Lab 7 decode values without opening serial, TCP, or device connections.
- Tool source: selected client and decoder tools still need versioned verification before release.

## Knowledge check

# Module 7 Quiz: Data Types And Word Order
## Questions

### Multiple Choice

1. A Modbus response contains data bytes `FF CE` for one register. Which statement is safest?
   - A. The value is always `65486`.
   - B. The value is always `-50`.
   - C. The bytes can be interpreted different ways depending on the source map.
   - D. The device sent an exception response.

2. A two-register value is documented as `U32, high word first` and returns `00 01 86 A0`. What is the expected unsigned value?
   - A. `100000`.
   - B. `2258632705`.
   - C. `-100000`.
   - D. `12.5`.

3. Which item is not carried as a universal meaning in the Modbus frame?
   - A. Function code.
   - B. Byte count in a read response.
   - C. Starting PDU address in a read request.
   - D. IEEE-754 float interpretation.

4. A known IEEE-754 float fixture returns `41 48 00 00`. The map says high word first. What should the client decode first?
   - A. Float `ABCD`.
   - B. Float `CDAB`.
   - C. Coil status.
   - D. Function code `06`.

5. A value looks wildly wrong, but the request function code, PDU address, and quantity match the source row, and the response function code and byte count are consistent. What is the best next move?
   - A. Change the address and the scale at the same time.
   - B. Assume the server is offline.
   - C. Test data type, signedness, word order, scale, and unit as separate hypotheses.
   - D. Treat the vendor word-order note as an official Modbus rule.

6. A single register returns data bytes `FF CE`. The source map says the value is signed 16-bit. What is the decoded value?
   - A. `-50`.
   - B. `65486`.
   - C. `-178`.
   - D. `50`.

7. The same register value `FF CE` is instead documented as unsigned 16-bit. What is the decoded value?
   - A. `-50`.
   - B. `65486`.
   - C. `65535`.
   - D. `206`.

### Short Answer

1. Decode `FF CE` as unsigned 16-bit and signed 16-bit.
2. Why is word order a vendor or profile convention rather than a universal Modbus rule?
3. List three source-map fields needed before a raw two-register value can be treated as engineering data.
4. Explain why a correct byte count does not prove a correct float value.

### Applied Scenario

A synthetic course device row says:

| Field | Value |
|---|---|
| Function code | `03` |
| Documented address | `40071` |
| PDU address | `70` |
| Quantity | `2` |
| Type | IEEE-754 32-bit float |
| Word order | high word first (`ABCD`) |
| Response PDU | `03 04 41 48 00 00` |

Tasks:

1. Identify the data bytes.
2. State the first decode assumption to try.
3. State the expected value.
4. Name two wrong assumptions that could still occur after a successful read.

## Answer Key

1. Multiple choice: C. The raw bytes need a source-map interpretation. A and B are both possible interpretations, but neither is universal without the map. D is wrong because `FF CE` is data bytes, not an exception-response shape.
2. Multiple choice: A. `0x000186A0` is `100000` decimal. B is the low-word-first interpretation, C invents a negative sign, and D is the separate float fixture value.
3. Multiple choice: D. IEEE-754 interpretation is layered on top of Modbus by a map, profile, tool, or verified behavior. Function code, response byte count, and request PDU address are protocol fields.
4. Multiple choice: A. The fixture states high word first, so decode bytes as `41 48 00 00`. The other order choices are test hypotheses only if the source row or known-value comparison contradicts the first decode.
5. Multiple choice: C. Keep the communication and interpretation hypotheses separate. Changing several assumptions at once destroys evidence, and vendor word order is not an official Modbus rule.
6. Multiple choice: A. `0xFFCE` as signed 16-bit two's-complement is `65486 - 65536 = -50`.
7. Multiple choice: B. `0xFFCE` as unsigned 16-bit is `65486`.

## Distractor Review Notes

| Question | Correct | Why The Distractors Are Wrong |
|---:|---|---|
| 1 | C | A and B each choose one interpretation as universal, and D mistakes data bytes for an exception response. |
| 2 | A | B uses the wrong word order, C adds signed interpretation not specified by the source row, and D belongs to a different float example. |
| 3 | D | A/B/C are Modbus message fields, while IEEE-754 meaning is defined by a source map, profile, tool setting, or verified behavior. |
| 4 | A | B is a possible test only if evidence contradicts the stated map, C changes the table and data model, and D is a write function code. |
| 5 | C | A changes multiple variables, B ignores the valid response evidence, and D turns a vendor convention into an official protocol rule. |
| 6 | A | B is the unsigned interpretation, C miscomputes the two's-complement value, and D drops the negative sign. |
| 7 | B | A is the signed interpretation, C is the all-ones value `0xFFFF`, and D keeps only the low byte `0xCE`. |

Short answer:

1. Unsigned 16-bit `FF CE` is `65486`; signed 16-bit two's-complement is `-50`.
2. Modbus defines how register data is transported, but device documentation or a profile defines how multiple registers combine into a larger value.
3. Good answers include type, width, signedness, byte/word order, scale, unit, validity context, documented address, PDU address, quantity, and source evidence.
4. A byte count only proves the number of data bytes returned. The client can still apply the wrong type, signedness, word order, scale, or unit.

Applied scenario:

1. The data bytes are `41 48 00 00`.
2. First try IEEE-754 float `ABCD`.
3. The expected value is `12.5`.
4. Wrong assumptions include low-word-first decode, byte-swapped decode, treating the bytes as a U32 integer, applying an unsupported scale, or changing the address before testing the decode.

## Source Notes

- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for multi-byte numeric encoding in protocol fields, four primary logical data tables, addressing model and zero-based PDU addresses, read holding/input register quantity limits, and normal response vs exception response.
- Signedness, IEEE-754 use, word order, strings, BCD, and wider numeric meanings remain map/profile/tool behavior unless tied to a reviewed source.

---
_Learn Modbus · learnmodbus.studioseventeen.io · Module 7/16 · v1.0.0 · Exported for offline / agent use._
