# Module 6: Register Maps

> Reading a vendor map and turning it into a reliable, reviewed integration plan.

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

## What you'll be able to do
- Normalize a vendor register map into a reviewable integration plan.
- Identify the seven columns every map needs (address, type, scale, unit, R/W, range, notes).
- Flag missing evidence before you put a single value on a screen.

## Three things people get wrong
1. **Skipping the 'evidence' column.** For every row, record where the meaning came from: spec page, lab read, vendor email, or assumption.
2. **Treating the map as fully tested.** Until you've round-tripped each row, assume nothing — especially scale and word order.
3. **Putting raw values on a dashboard.** Dashboards lie when the map lies. Get the map reviewed first.

## From the field
**The map that needed a footnote on every row**

A bright integrator built a clean spreadsheet from a vendor PDF — and shipped it. Half the rows had 'scale: 1' by default because the column was blank in the source. Three weeks later, a 'voltage' reading of 12,460 turned out to be tenths of a volt. The fix was a footnote column, not new code.

## References
- [Modbus Application Protocol V1.1b3](https://www.modbus.org/file/secure/modbusprotocolspecification.pdf) — No standard register map — entirely vendor-specific.

## Lesson

# Module 6 Lesson: Register Maps
## Learner Outcome

By the end of this module, learners can read an OEM register map as evidence rather than a magic list of numbers. They can infer table, function code, documented address, actual PDU address, quantity, permission, data type, scale, unit, validity notes, and unresolved assumptions needed for a reliable integration.

## Key Concepts

- A register map is vendor documentation, not the Modbus protocol itself.
- Column names such as `Address`, `Register`, `Offset`, `Location`, `Word`, `Parameter`, and `Index` are not standardized.
- The word `register` is ambiguous until the table and function code are known.
- A professional map preserves both the vendor reference and the actual request fields used by the client.
- Reserved ranges, gaps, model-dependent registers, firmware changes, and hidden installer-only items are normal hazards.
- Evidence matters: record source row, address-base assumption, request bytes, response bytes, decoded value, and unresolved assumptions.

## Key Terms

- Register map: the source document or table that gives Modbus points meaning.
- Normalized row: a course/internal row that turns source-map wording into poll-ready fields and assumptions.
- Source label: the evidence category for a row, such as official rule, vendor-specific note, synthetic fixture, or needs verification.
- Access: whether the point is read-only, writable, reserved, or constrained.
- Validity condition: the state, mode, or context required for a value to be meaningful.
- Unresolved assumption: a map interpretation that must be tested or reviewed before release.

## Source-Grounded Explanation

Modbus defines logical tables, function codes, address fields, quantities, and response shapes. It does not define the meaning of a vendor's row named `Frequency`, `Status`, `Offset`, or `Register`.

That split is the heart of register-map work:

| Layer | Governs | Example |
|---|---|---|
| Official protocol | Function code, PDU address, quantity, response structure, exception behavior. | Function `04`, PDU address `20`, quantity `1`, normal response `04 02 17 70`. |
| Vendor-specific documentation | Meaning, type, scale, unit, permissions, validity, word order, and safety context. | `30021`, `U16`, `/100`, `Hz`, valid when grid present. |
| Tool behavior | Address-entry mode, byte/word-order labels, logs, export format. | Tool expects `20`, `21`, or `30021` depending on configuration. |
| Field evidence | What actually worked in a controlled test. | Captured request, response, decoded value, and comparison to known display. |

Learners should normalize every useful row into a poll-ready shape before configuring a client. The clean row should answer:

| Question | Why It Matters |
|---|---|
| Which logical table? | Determines likely read/write function code. |
| Which function code? | Prevents mixing holding registers and input registers. |
| Which documented address? | Preserves the vendor evidence. |
| Which actual PDU address? | Prevents off-by-one errors. |
| What quantity? | Multi-register values must read the correct number of registers. |
| What type, scale, and unit? | Converts raw words into useful values. |
| What permission and safety context? | Avoids unsafe writes and rejected requests. |
| What validity context? | Prevents displaying stale, sentinel, or state-dependent data as real telemetry. |

## Practical Example

Synthetic source row:

| Parameter | Register | Access | Format | Multiplier | Unit | Notes |
|---|---:|---|---|---:|---|---|
| AC frequency | `30021` | R | `U16` | `/100` | Hz | Input register table |

Normalized row:

| Field | Value |
|---|---|
| Transport | Modbus TCP for the no-hardware example; PDU fields also apply to RTU |
| Logical table | Input registers |
| Function code | `04` Read Input Registers |
| Documented address | `30021` |
| Actual PDU address | `20` |
| Quantity | `1` register |
| Data type | unsigned 16-bit |
| Scale/unit | raw `/100 Hz` |
| Expected normal response | function `04`, byte count `02`, data bytes `17 70` |
| Decoded value | `0x1770 = 6000`; `6000 / 100 = 60.00 Hz` |
| Likely failure or exception | exception `02` if address/range unavailable; wrong table if function `03` is used |
| Source label | Synthetic vendor note plus official protocol request fields |

Request PDU:

```text
04 00 14 00 01

04      function code: Read Input Registers
00 14   starting PDU address: 20
00 01   quantity: 1 register
```

Normal response PDU:

```text
04 02 17 70

04      function code echoed
02      byte count: 2
17 70   raw register value: 6000 decimal
```

## Common Pitfalls

- Treating every row called `Register` as a holding register.
- Assuming read-only measurements must use function code `04`; many devices expose read-only values through holding registers and function `03`.
- Reading across reserved gaps because one large poll is convenient.
- Losing the documented address after converting to PDU address.
- Recording type and scale but omitting unit and validity context.
- Copying write-capable rows into a tool without reviewing safety state, permissions, interlocks, and rollback.
- Treating a synthetic or vendor-specific map convention as a universal Modbus rule.

## Instructor Flow

1. Start with the split between official protocol and vendor meaning.
2. Give learners the messy synthetic course map first.
3. Have them normalize one easy row, one input-register row, one 32-bit row, one bitfield, and one ambiguous offset row.
4. Force source labels: official rule, synthetic vendor note, tool behavior, field note, or needs verification.
5. End by comparing normalized rows to the register-map review checklist.

## Check-Your-Understanding

1. A map heading says `Holding Registers`, but the row access is read-only. Which function code is the first one to try, and why?
2. A map lists `Offset 60` with no base convention. What should you record before testing it?
3. Why should a normalized map preserve both documented address and actual PDU address?
4. A contiguous read fails across a reserved range, but smaller reads succeed. What should the polling plan do next?

## Source Notes

- Official rule: Modbus Application Protocol Specification V1.1b3 defines logical data tables, function codes, address fields, quantity fields, response shapes, and exception behavior.
- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for four primary logical data tables, addressing model and zero-based PDU addresses, public function-code categories, normal response vs exception response, and exception codes and exception response PDU shape.
- Vendor-specific note: register meanings, data type conventions, word order, scale, unit, permission, and validity context come from vendor documentation or verified behavior.
- Synthetic source: `release/synthetic-course-device-map.md` provides course-authored examples for no-hardware lessons.
- Local traceability: see `sources/source-traceability-matrix.md`, Modules 6 Register Maps.

## Completion Checkpoint

Before moving on, learners should normalize at least one weak register-map row and mark missing table, address base, data type, scale, access, or source evidence as `needs verification` instead of guessing.

## Reusable Assets

- [Synthetic course device map](https://learnmodbus.studioseventeen.io/resource/synthetic-course-device-map)
- [Register-map review checklist](https://learnmodbus.studioseventeen.io/resource/register-map-review-checklist)
- [Device survey template](https://learnmodbus.studioseventeen.io/resource/device-survey-template)
- [Register-map normalization lab](https://learnmodbus.studioseventeen.io/lesson/register-maps?page=lab-1)
- [Module 6 quiz](https://learnmodbus.studioseventeen.io/lesson/register-maps?page=quiz)

## Labs

### Lab 1

# Lab 22: Register Map Normalization
## Learner Outcome

Learners can convert a messy OEM-style register map excerpt into a clean integration table that records function code, documented address, PDU address, quantity, type, scale, unit, access, validity context, evidence, and unresolved assumptions.

## Prerequisites

- Module 3: data tables.
- Module 4: function codes.
- Module 5: addressing.
- Module 6 lesson through the AC frequency worked example.

## Safety And Scope

This lab uses [Synthetic Course Device Map](https://learnmodbus.studioseventeen.io/resource/synthetic-course-device-map). It is not a real vendor map and should not be used to configure production equipment.

Write rows are discussion-only unless a simulator later implements them. Do not perform writes against real devices during this lab.

## Scenario

You receive an OEM-style table for a course energy meter simulator. The table mixes `40001` notation, `30021` notation, a reserved gap, one 32-bit value, one status word, and one ambiguous `Offset 60` row.

Your job is to create a normalized map that another integrator could use without guessing.

## Protocol Target

The required worked row is `AC frequency`:

| Field | Value |
|---|---|
| Transport | Modbus TCP paper exercise; PDU fields also apply to RTU |
| Function code | `04` Read Input Registers |
| Documented address | `30021` |
| Actual PDU address | `20` |
| Quantity | `1` register |
| Data type | unsigned 16-bit |
| Scale/unit | raw `/100 Hz` |
| Expected normal response | `04 02 17 70`, raw `6000`, decoded `60.00 Hz` |
| Likely failure mode | exception `02` if address/range is unavailable; wrong table if function `03` is used |

## Procedure

1. Open `release/synthetic-course-device-map.md`.
2. Copy the OEM-style source excerpt into your notes.
3. For each row, infer the logical table, likely function code, documented address, PDU address, quantity, data type, scale/unit, access, and validity context.
4. Mark each row's source label as `Official rule`, `Synthetic vendor note`, `Tool behavior`, `Field note`, or `Needs verification`.
5. Do not normalize reserved ranges into active polling rows.
6. Mark the ambiguous `Offset 60` row as needing verification rather than pretending the base is known.
7. Compare your normalized rows with the answer key.

## Worksheet

| Tag | Function | Documented Address | PDU Address | Qty | Type | Scale/Unit | Access | Validity | Evidence | Source Label | Unresolved Assumptions |
|---|---:|---|---:|---:|---|---|---|---|---|---|---|
| `device_status_word` |  |  |  |  |  |  |  |  |  |  |  |
| `line_voltage_l1_l2` |  |  |  |  |  |  |  |  |  |  |  |
| `ac_frequency` |  |  |  |  |  |  |  |  |  |  |  |
| `total_energy_import` |  |  |  |  |  |  |  |  |  |  |  |
| `command_word` |  |  |  |  |  |  |  |  |  |  |  |
| `temperature` |  |  |  |  |  |  |  |  |  |  |  |

## Expected Normalization Highlights

- `40001` usually maps to holding-register PDU address `0`, function `03`, quantity `1`.
- `40010` usually maps to holding-register PDU address `9`, function `03`, quantity `1`, unsigned 16-bit, `/10 V`.
- `30021` usually maps to input-register PDU address `20`, function `04`, quantity `1`, unsigned 16-bit, `/100 Hz`.
- `40031` is a two-register value starting at PDU address `30`, function `03`, quantity `2`, unsigned 32-bit, high word first.
- `40101` is writable only in the simulator context and should carry write-safety notes.
- `Offset 60` is unresolved until the manual or test evidence proves whether it is zero-based or one-based and which table/function applies.

## Expected Observations

- Learners who preserve both documented address and PDU address can explain their request.
- Learners who mark `Offset 60` as unresolved have done the safer professional thing.
- Learners who poll across `40050-40059` should identify the reserved gap before blaming the device.
- Learners should separate official protocol fields from synthetic map meanings.

## Troubleshooting Notes

- Wrong table: function `03` and function `04` can both read 16-bit registers, but they address different logical tables.
- Wrong address base: a one-off request can return a wrong-but-valid adjacent value.
- Reserved gap: a large contiguous read may fail even if smaller reads around the gap succeed.
- Unsafe write: the `command_word` row is simulator-only until a write-safety workflow is reviewed.

## Answer Key

| Tag | Function | Documented Address | PDU Address | Qty | Type | Scale/Unit | Access | Validity | Evidence | Source Label | Unresolved Assumptions |
|---|---:|---|---:|---:|---|---|---|---|---|---|---|
| `device_status_word` | `03` | `40001` | `0` | `1` | unsigned 16-bit bitfield | none | R | bit definitions are synthetic | synthetic map row | Synthetic vendor note plus official protocol fields | none for paper lab |
| `line_voltage_l1_l2` | `03` | `40010` | `9` | `1` | unsigned 16-bit | `/10 V` | R | valid when grid present | synthetic map row and response `03 02 09 C4` | Synthetic vendor note plus official protocol fields | simulator output pending |
| `ac_frequency` | `04` | `30021` | `20` | `1` | unsigned 16-bit | `/100 Hz` | R | input register table | synthetic map row and response `04 02 17 70` | Synthetic vendor note plus official protocol fields | simulator output pending |
| `total_energy_import` | `03` | `40031` | `30` | `2` | unsigned 32-bit high word first | `Wh` | R | high-word-first stated in synthetic map | synthetic map row and response example | Synthetic vendor note plus official protocol fields | simulator output pending |
| `command_word` | `06` or `16` in simulator only | `40101` | `100` | `1` | unsigned 16-bit bitfield | none | RW | simulated only; preserve reserved bits | synthetic map row | Synthetic write-safety note | write workflow not runnable yet |
| `temperature` | Needs verification | `Offset 60` | Needs verification | `1` | signed 16-bit | `/10 deg C` | R | unclear offset base | synthetic map row only | Needs verification | table/function and address base unresolved |

## Check Questions

1. Why is `ac_frequency` read with function `04` rather than `03`?
2. Why should `Offset 60` remain marked `Needs verification`?
3. Why is reading `40050-40059` as part of one large contiguous poll a bad first plan?
4. What evidence would move this lab from paper exercise to runnable no-hardware lab?

## Instructor Notes

- Do not reward learners for filling every cell with certainty. Correctly marking ambiguity is part of the skill.
- Ask learners to explain whether each claim is protocol behavior, synthetic map behavior, or tool behavior.
- Use this lab as the bridge into Module 7 data types and Module 14 troubleshooting.

## Source Notes

- Official source: Modbus Application Protocol Specification V1.1b3 for function codes, PDU address fields, quantities, and response shapes.
- Synthetic source: `release/synthetic-course-device-map.md` for map meanings and example values.
- Local source: `release/register-map-review-checklist.md` for row fields and review prompts.

## Knowledge check

# Module 6 Quiz: Register Maps
## Questions

### Multiple Choice

1. A vendor row is labeled `Register 40010`. Which statement is safest?
   - A. The client sends `40010` as the PDU address.
   - B. The row is probably a holding-register reference, but the actual PDU address and tool mode must be confirmed.
   - C. The row must be read with function `04`.
   - D. The row is writable because it starts with `4`.

2. A map heading says `Input Registers` and a row lists `30021`, `U16`, `/100 Hz`. What is the first request to try?
   - A. Function `04`, PDU address `20`, quantity `1`.
   - B. Function `03`, PDU address `30021`, quantity `1`.
   - C. Function `04`, PDU address `30021`, quantity `100`.
   - D. Function `06`, PDU address `20`, quantity `1`.

3. Which field is vendor-specific rather than a Modbus wire-protocol field?
   - A. Function code.
   - B. PDU address.
   - C. Unit scale `/10 V`.
   - D. Quantity.

4. A row says `Offset 60` but does not identify table or address base. What should the normalized map say?
   - A. Holding register, PDU address `59`.
   - B. Input register, PDU address `60`.
   - C. Needs verification.
   - D. Broadcast address.

5. Why should reserved gaps be handled carefully?
   - A. The Modbus protocol requires all reserved gaps to be writable.
   - B. A contiguous read across unsupported addresses may fail even if smaller reads succeed.
   - C. Reserved gaps always contain zero.
   - D. Reserved gaps are always coils.

6. A map row reads `Total energy import`, `40031`, `U32`, high word first. What normalized function code, first PDU address, and quantity should you record?
   - A. Function `04`, PDU address `31`, quantity `1`.
   - B. Function `03`, PDU address `30`, quantity `2`.
   - C. Function `03`, PDU address `40031`, quantity `2`.
   - D. Function `03`, PDU address `31`, quantity `1`.

7. A map heading says `Holding Registers`, but a row's access is read-only. Which read function code should you try first, and why?
   - A. Function `04`, because read-only values always live in input registers.
   - B. Function `03`, because a vendor can expose read-only measurements in holding-register space.
   - C. Function `02`, because read-only implies a discrete input.
   - D. Function `06`, because read-only rows still need a write to refresh.

### Short Answer

1. List four fields that a normalized register-map row should preserve.
2. Explain why a read-only value may still appear in holding registers and use function `03`.
3. What evidence would you record to prove the actual PDU address used by a client?
4. Why is a vendor manual authoritative only for its named product/version/configuration?

### Applied Scenario

Synthetic source row:

| Parameter | Register | Access | Format | Multiplier | Unit | Notes |
|---|---:|---|---|---:|---|---|
| Total energy import | `40031` | R | `U32` | `1` | Wh | High word first |

Tasks:

1. Fill the normalized fields: function code, documented address, PDU address, quantity, type, scale/unit, access, and word order.
2. Name one likely failure or wrong-value mode.
3. State whether the word-order claim is official protocol behavior or synthetic vendor-note behavior.

## Answer Key

1. Multiple choice: B. `40010` is documentation notation; the function code and PDU address must be inferred and tested.
2. Multiple choice: A. `30021` usually means input-register item 21, so function `04`, PDU address `20`, quantity `1`.
3. Multiple choice: C. Scale and unit come from vendor documentation or synthetic teaching data, not the Modbus frame.
4. Multiple choice: C. Without table and address-base evidence, the row should remain unresolved.
5. Multiple choice: B. Unsupported gaps can produce exception `02` or force smaller polling groups.
6. Multiple choice: B. `40031` is holding-register item 31, so function `03`, PDU address `30`; a `U32` value spans two registers, so quantity `2`.
7. Multiple choice: B. Modbus allows holding registers to be read with function `03`; a vendor can expose read-only measurements in holding-register space even when writes are not permitted.

## Distractor Review Notes

| Question | Correct | Why The Distractors Are Wrong |
|---:|---|---|
| 1 | B | A sends documentation notation as an offset, C picks the input-register function without evidence, and D assumes write access from a prefix. |
| 2 | A | B uses the holding-register function and sends the documented number, C sends the documented number and invents quantity `100`, and D is a write function. |
| 3 | C | A/B/D are Modbus request or response fields, while scale and unit come from a source map or teaching fixture. |
| 4 | C | A/B invent table and address-base assumptions, and D confuses an unresolved register-map offset with the serial broadcast address. |
| 5 | B | A invents a write rule, C invents data content, and D changes reserved register gaps into coil items without evidence. |
| 6 | B | A picks the input-register function and forgets to subtract one, C sends the documented number as a PDU address, and D forgets that a `U32` value needs two registers. |
| 7 | B | A assumes read-only values must be input registers, C confuses read-only with the discrete-input table, and D uses a write function code to read a value. |

Short answer:

1. Good answers include documented address, PDU address, function code, quantity, data type, scale/unit, access, validity notes, source evidence, and unresolved assumptions.
2. Modbus allows holding registers to be read with function `03`; a vendor can expose read-only measurements in holding-register space even if writes are not permitted.
3. Request bytes, packet capture, raw client log, simulator log, or a tool display showing function code and PDU address.
4. The manual describes that product's implementation choices; data meanings, scale, word order, permissions, and quirks are not universal Modbus rules.

Applied scenario:

1. Function `03`; documented address `40031`; PDU address `30`; quantity `2`; unsigned 32-bit; scale/unit `1 Wh`; access read-only; high word first.
2. Wrong word order can produce an implausible energy value; a contiguous read across a reserved gap can produce exception `02`; address-base error can return an adjacent value.
3. Word order is synthetic vendor-note behavior in this course map, not an official Modbus protocol rule.

## Source Notes

- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for four primary logical data tables, addressing model and zero-based PDU addresses, public function-code categories, read holding/input register quantity limits, exception codes and exception response PDU shape, and multi-byte numeric encoding in protocol fields.
- Register meanings, word order, scale, access, and vendor row labels remain vendor-specific or synthetic-map behavior.

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