— Conventions, Offsets, Vendor Quirks & Mapping Best-Practices
(Module 9 · Advanced & Specialised Topics)
Learning objectives
- Master every published addressing convention—0-based, 1-based, 30 000/40 000 legacy, PNO “x.y.z register” style, and prefix notation (
4x00017
). - Diagnose & fix vendor-specific quirks: Allen-Bradley word-swap + offset, Danfoss “decade code”, Siemens S7 byte addressing inside word confusion, Schneider floating blocks, ABB “tabbed offsets”.
- Design register maps that scale to ≥ 65 536 addresses without ambiguity—using banked tables, file records, or 32-bit “extended register” patterns.
- Document maps in a future-proof way (semantic YAML + auto-generated XLS/PDF) and version-control every change.
- Automate conversion and validation with open-source tools (
modbus-map-linter
,yaml-to-excel.py
,Node-RED RegMap validator
).
27.1 The three core numbering schemes
Scheme | Human example (Holding Reg) | PDU start address | Comment |
---|---|---|---|
Legacy 1-based | 40017 | 0x0010 | Most manuals pre-2000; still common |
Prefix notation | 4x0017 (or 4X:17 ) | 0x0010 | Unambiguous; recommended |
IEC-PNO | 4.17 (table.index) | 0x0010 | Popular in Profinet docs |
Mnemonic: “Remove the table prefix, subtract one.”
27.2 0-based vs 1-based under the hood
27.2.1 Derivation
PDU_offset = Human_number – Table_base – 1 (for 1-based docs)
Human_number = PDU_offset + Table_base + 1 (for generating manual)
Table_base = 0 (0X), 10000 (1X), 30000 (3X), 40000 (4X).
27.2.2 Pitfall pattern
Symptom: All reads return neighbour value or zero.
Signature: (desired_value << 16) == returned_word
often true.
Fix: Subtract 1 from start address; retest.
(Fig-27-1 placeholder: off-by-one ladder.)
27.3 Vendor-specific wrinkles
Vendor | Quirk | Example | Mitigation |
---|---|---|---|
Rockwell Micro-820 | Start address 400001 | 400001 → PDU 0 | Subtract 1 000 00 then 1 |
Danfoss FC302 VFD | Registers “0-99” internal mapping then +1k offset | Parameter 16-01 → HR 16101 | Use vendor CSV converter |
Siemens S7 1200 | Byte addressing inside word (DBX ) | DB10.DBW4 → HR40003 but high/low bytes swapped | Map with word-swap flag |
ABB Totalflow | Tabs: 4.x.y → file records | Gas Vol 4.12.3 | Use FC 20/21 not 03 |
Wago 750-880 | Bit addressing via “X” suffix | 4x0002.3 = bit 3 of HR2 | Mask in gateway |
(Table 27-A; keep growing in Appendix A6.)
27.4 Large address-space strategies
27.4.1 Banked tables
Divide 0–65 535 into “banks” of 10 000.
- Bank 0: Process real-time (0…9999)
- Bank 1: Config (10000…19999)
- Bank 2: Historical snapshot (20000…)
Master can burst-poll each bank; easier CoV diff.
27.4.2 File Record model (FC 20/21)
Field | Size | Meaning |
---|---|---|
File Num | 2 B | “Block ID” (0…65535) |
Record Num | 2 B | Index inside block |
Length | 2 B | Words |
Use-case: 600 k-word historian; leaves 4X unpolluted.
27.4.3 32-bit “extended register” convention
- Some vendors let FC 23 address
>125
registers by treating “word count” as 32-bit across two bytes. - Not standard; always pair with vendor API or update SCADA driver.
(Fig-27-2: extended register framing.)
27.5 Best-practice mapping patterns
Pattern | Pros | Cons | Thickness |
---|---|---|---|
Dense numeric (classic) | Minimal doc overhead | Off-by-one risk; zero semantics | Thin |
Prefixed blocks (AI , AO , DI , DO ) | Visual separation of I/O types | Extra gaps → wasted addresses | Medium |
Semantic YAML (name: , unit: ) | Machine-readable, auto-docs | Needs generator tooling | Thick (future-proof) |
27.5.1 YAML snippet example
- reg: 40001
name: TankLevel
type: float32
unit: "%"
byte_order: "CDAB"
rw: R
desc: Raw ultrasonic tank level, scaled 0-100
- reg: 00001
name: PumpA_Enable
type: coil
rw: RW
Run yaml-to-excel.py registers.yaml map.xlsx
→ generates colour-coded XLS for manual + JSON for SCADA auto-import.
(Listing 27-1: Python generator, 80 LOC).
27.6 Documentation & version control strategy
- Source of truth: YAML/CSV in Git.
- Auto-generate:
- PDF register book (pandoc).
- XLS for non-tech users.
- HTML interactive table (DataTables).
- CI linter (
modbus-map-lint
) checks:- Duplicated addresses.
- Hole > n words.
- Illegal type/byte order combo.
- Release tag vMajor.Minor.Patch; bump patch on non-breaking re-order, major when addresses change.
(Fig-27-3: CI pass/fail dashboard.)
27.7 Validation techniques
Technique | Tool | Goal |
---|---|---|
Golden frame | pymodbus.framer | Confirm PDU vs spec after firmware change |
Auto-echo | Device responds with same data to “test register” | Detect word-swap early |
SCADA import diff | Script compares SCADA tag DB ↔ YAML | Blocks off-by-one rollouts |
Fuzz read | modbus-fuzz-addr.py sweeps entire 0-65 535 | Reveal undocumented registers (security review) |
27.8 Case-study: Resolving 2 000-register overlap in 24 h
Factory X added new heat-treat oven; vendor used 40001…40030
already occupied by historian. Symptoms: historian data flickered every 5 s.
Steps:
- YAML diff: overlap highlighted red.
- Temp fix: firewall rule blocks oven write FC 16 to conflicting range.
- Vendor patched firmware → shifted oven registers to bank 4.
- CI linter gate added to integration pipeline to prevent recurrence.
Result: 30 min downtime vs projected 12 h manual debug.
27.9 Best-practice checklist
✔︎ | Rule |
---|---|
☐ | Use prefix notation 4x/3x/1x/0x — no bare numbers. |
☐ | Reserve 100+ spare registers per bank for growth. |
☐ | Commit YAML/CSV map to Git; tag releases with device firmware. |
☐ | Document byte+word order per multi-word entry. |
☐ | Validate map with linter before flashing or shipping device. |
☐ | Provide machine-readable export for SCADA and edge gateways. |
☐ | Never overload same address for two units (even if “read” vs “write”). |
Chapter recap
Data-address mastery is not trivia—it’s the difference between stable 24 × 7 operation and phantom faults that drain weeks. With formal map generation, rigorous version control, and linting, you bullet-proof both engineering workflows and cybersecurity posture.
Assets to create
ID | Visual / file |
---|---|
Fig-27-1 | Off-by-one ladder / equation graphic |
Fig-27-2 | Extended-register frame diagram |
Fig-27-3 | CI linter pass/fail dashboard |
Table 27-A | Vendor quirk cheat-sheet |
Listing 27-1 | YAML-to-Excel generator |
Next: Chapter 28 – Performance Optimisation for Modbus Networks will quantify throughput limits, poll-rate calculus, and smart batching, then deliver code and oscilloscope proof for each optimisation lever.