Modbus Register Map Reading Guide for BAS

ModbusModbus registersregister mapaddressingVFD
April 11, 2026|8 min read

A Modbus register map is a table published by the device manufacturer that tells you the address, data type, and scaling of every readable or writable value in the device. To use it correctly, determine whether the document uses 0-based (protocol-level) or 1-based (data-model) addressing, match the register type to the correct Modbus function code, and pay attention to byte order—getting any one of these wrong produces garbage data or silent off-by-one errors.

What Is a Modbus Register Map

Every Modbus device—VFD, energy meter, chiller controller, boiler plant manager—exposes its internal data through a fixed set of numbered registers. The register map (sometimes called a register table or point list) is the manufacturer's documentation that defines what lives at each address. It tells you which register number holds the supply air temperature, which one holds the compressor run status, and which one you write to in order to change a setpoint.

Without the register map, a Modbus device is a black box. The protocol itself carries no metadata—when you read register 40001, the response is just a raw 16-bit integer. The register map is what gives that integer meaning: it's a temperature in tenths of a degree Celsius, or a frequency in hundredths of a Hertz, or a bitmask where bit 3 means “fault active.”

Register maps vary wildly in quality. Some manufacturers provide clean tables with address, name, data type, units, scaling factor, and read/write access. Others hand you a scanned PDF with ambiguous addressing and no mention of byte order. Learning to read both kinds is a core skill for BAS integration work.

Modbus Register Types

The Modbus data model defines four distinct blocks of addressable data. Each block serves a different purpose and uses different function codes for access:

Register TypeData SizeAccessClassic Address RangeFunction Codes
Coils1 bitRead / Write00001–0999901 (Read), 05 (Write Single), 15 (Write Multiple)
Discrete Inputs1 bitRead Only10001–1999902 (Read)
Input Registers16 bitsRead Only30001–3999904 (Read)
Holding Registers16 bitsRead / Write40001–4999903 (Read), 06 (Write Single), 16 (Write Multiple)

Coils are single-bit outputs—think relay states, enable/disable flags, and on/off commands. Discrete inputs are single-bit read-only values like digital status contacts or alarm flags. Input registers hold 16-bit read-only measurements such as sensor readings and metered values. Holding registers are 16-bit read/write registers used for setpoints, configuration parameters, and command values.

In BAS work, you will spend most of your time with holding registers (function codes 03 and 16) and input registers (function code 04). Coils and discrete inputs show up less often in HVAC equipment, though VFDs sometimes use coils for run/stop and direction commands.

Modbus Register Addressing: 0-Based vs 1-Based

This is where most off-by-one errors originate. The Modbus specification defines two different numbering schemes, and manufacturers use them inconsistently:

The relationship is straightforward: PDU address = data model address − offset. For holding registers, that offset is 40001. For input registers, 30001. For coils, 1. For discrete inputs, 10001.

Worked Example

A chiller register map lists “Leaving Chilled Water Temperature” at register 40003. To read this value:

  1. The prefix “4” tells you it is a holding register, so you use function code 03.
  2. The PDU address is 40003 − 40001 = 2.
  3. You send a Read Holding Registers request with starting address 2 and quantity 1.

If the same register map had listed this as register 2 without a prefix and stated “Holding Register,” the PDU address is already 2—no subtraction needed. But if the map lists it as register 3 and says “1-based,” you subtract 1 to get PDU address 2.

The fix is always the same: read the front matter of the register map. Look for a note that says “addresses are 0-based” or “register 1 = PDU address 0.” If the document uses five-digit numbers starting with 0, 1, 3, or 4, it is almost certainly using 1-based data model addressing. If it uses plain numbers starting from 0, it is 0-based. When in doubt, read register 0 and register 1 and compare the values against what the device is actually doing—that resolves the ambiguity fast.

Data Types and Byte Order

A single Modbus register is 16 bits (2 bytes), which limits it to unsigned integer values from 0 to 65535. Real-world data often requires larger or more precise representations, so manufacturers pack data across multiple registers or apply scaling factors:

Data TypeRegister CountValue RangeCommon Usage
UINT1610 to 65,535Status codes, small counts, scaled temperatures
INT161−32,768 to 32,767Temperatures, power factor (signed)
UINT3220 to 4,294,967,295Energy totals (kWh), runtime hours
INT322−2,147,483,648 to 2,147,483,647Bidirectional power, signed totals
FLOAT32 (IEEE 754)2±3.4 × 10³&sup8;Voltages, currents, temperatures (high precision)
UINT6440 to 1.8 × 10¹&sup9;Cumulative energy in watt-hours

When a value spans two registers (32-bit types), byte order matters. Modbus defines bytes within a single register as big-endian (high byte first), but the standard says nothing about which register comes first when two registers combine into a 32-bit value. The result is four possible byte orderings:

Most HVAC and metering equipment uses big-endian (AB CD) or word-swapped (CD AB). If you read a 32-bit float and get a nonsensical value like 2.37 × 10²&sup6; when you expected 72.4°F, try swapping the register order before troubleshooting anything else. Tools like PyModbus let you specify byte order explicitly with the BinaryPayloadDecoder class using the byteorder and wordorder parameters.

Reading a Manufacturer Register Map: Step-by-Step

Here is a realistic excerpt from an energy meter register map (based on common Eastron SDM630 documentation):

┌──────────┬────────────────────────────┬───────────┬────────┬────────┐
│ Register │ Description                │ Data Type │ Units  │ Access │
├──────────┼────────────────────────────┼───────────┼────────┼────────┤
│ 30001    │ Phase 1 Line to Neutral V  │ FLOAT32   │ Volts  │ R      │
│ 30003    │ Phase 2 Line to Neutral V  │ FLOAT32   │ Volts  │ R      │
│ 30005    │ Phase 3 Line to Neutral V  │ FLOAT32   │ Volts  │ R      │
│ 30007    │ Phase 1 Current            │ FLOAT32   │ Amps   │ R      │
│ 30009    │ Phase 2 Current            │ FLOAT32   │ Amps   │ R      │
│ 30011    │ Phase 3 Current            │ FLOAT32   │ Amps   │ R      │
│ 30013    │ Phase 1 Power              │ FLOAT32   │ Watts  │ R      │
│ 30025    │ Phase 1 Power Factor       │ FLOAT32   │ (none) │ R      │
│ 30071    │ Total System Power         │ FLOAT32   │ Watts  │ R      │
│ 30343    │ Total Import kWh           │ FLOAT32   │ kWh    │ R      │
└──────────┴────────────────────────────┴───────────┴────────┴────────┘

Note: Addresses are 1-based. Register 30001 = Input Register PDU address 0.
       All values are IEEE 754 floating point, big-endian (AB CD) byte order.

Walk through the map systematically:

  1. Identify the register type. The “3xxxx” prefix tells you these are input registers. You will use function code 04 (Read Input Registers) to access them.
  2. Determine the addressing convention. The footer note says addresses are 1-based and that register 30001 maps to PDU address 0. So to read Phase 1 voltage at register 30001, you send a request with starting address 0.
  3. Account for multi-register values. Each value is FLOAT32, which occupies two consecutive registers. Phase 1 voltage sits in registers 30001–30002 (PDU addresses 0–1). Phase 2 voltage starts at 30003 (PDU address 2). To read all three phase voltages in one request, start at PDU address 0 and read 6 registers.
  4. Note the byte order. The map specifies big-endian (AB CD). Configure your Modbus master or decoder accordingly. In PyModbus, this means byteorder=Endian.BIG and wordorder=Endian.BIG.
  5. Check for gaps. Notice the jump from register 30013 to 30025 and from 30025 to 30071. Not every register address is populated. Reading an undefined register may return zero, throw an exception, or return stale data—the behavior is manufacturer-specific. Only read addresses that are documented.
  6. Confirm access type. All entries here are marked “R” (read-only). Attempting a write to an input register will return a Modbus exception code 01 (Illegal Function).

Applying a Scaling Factor

Not all register maps use floating-point values. Many VFD and chiller manufacturers store temperatures and frequencies as scaled integers. For example, a VFD register map might list:

Register 40108 | Output Frequency | UINT16 | Units: 0.01 Hz | R

A raw value of 5985 means 5985 × 0.01 = 59.85 Hz. If you forget the scaling factor and pass 5985 into your BAS as-is, your trend logs will show a motor running at an impossible frequency and your alarms will fire continuously. Always check the “Units” or “Scale” column.

Common Modbus Register Map Mistakes

  1. Off-by-one addressing errors. The number one source of bad reads. A register map says “register 100” but does not specify 0-based or 1-based. You configure PDU address 100, but the manufacturer meant data model address 100, which is PDU address 99. The result: you read the wrong register and get a value that looks plausible but is actually the adjacent parameter. Validate every integration by comparing read values against the device's local display.
  2. Wrong function code. Using function code 03 (Read Holding Registers) to read an input register (which requires function code 04) returns a Modbus exception or reads from a completely different address space. The register type dictates the function code—there is no shortcut.
  3. Ignoring byte order on 32-bit values. You configure a FLOAT32 read and get a value of 1.27 × 10¹² instead of 120.5. The registers are in the wrong order. Swap the word order and reread. This happens constantly with energy meters from different manufacturers because there is no universal standard for register ordering.
  4. Reading across register gaps. You request 20 consecutive registers starting at address 0, but only addresses 0–5 and 10–15 are defined. Some devices return exception code 02 (Illegal Data Address) for the gap. Others return zeros. Still others return the last known value in the undefined range. Break your reads into separate requests that match the documented register blocks.
  5. Forgetting scaling factors. A raw value of 2500 with a scale of 0.1 means 250.0°F, not 2500°F. This mistake creates absurd trend data and nuisance alarms. Worse, if you write a setpoint without applying the inverse scale, you command the device to a value 10x higher or lower than intended. Always multiply on reads and divide on writes (or vice versa, depending on how the manufacturer defines the factor).

Platform Compatibility

Modbus register maps are protocol-level documentation—they apply regardless of which BAS platform or integration tool you use. The critical settings that must match the register map are the function code, PDU address, data type, byte order, and scaling factor. Here is how common platforms handle these:

Platform / ToolAddress ConventionByte Order ConfigNotes
Tridium Niagara (JACE / Supervisor)0-based PDU addressingPer-point “Byte Order” propertyNiagara's Modbus driver uses 0-based addresses. If the register map says 40001, enter address 0 and select “Holding Register.”
Johnson Controls (Metasys / FEC)1-based data modelConfigured in integration objectMetasys uses 1-based addressing in the integration definition. Register 40001 is entered as 40001.
Schneider Electric (EcoStruxure / SmartStruxure)0-based PDU addressingPer-device settingThe EcoStruxure Modbus driver expects 0-based addressing with function code selection.
Honeywell (Spyder / WEBs-AX)Varies by driver versionGlobal or per-deviceCheck the driver documentation for your specific firmware version. Some older revisions use 1-based internally.
PyModbus (Python library)0-based PDU addressingBinaryPayloadDecoder with explicit byteorder and wordorderAll PyModbus read/write methods use 0-based addresses. client.read_holding_registers(0, 2) reads the first two holding registers.
Modbus Poll / Modbus Scan (diagnostic tools)Configurable (0 or 1-based)Configurable per scan definitionSet the “PLC Addresses” checkbox in Modbus Poll to toggle between 0-based and 1-based display. Use this tool to validate register reads before committing to your BAS configuration.

The single most important step in any Modbus integration is confirming whether your platform uses 0-based or 1-based addressing, then adjusting the register map values accordingly. Get this wrong and every point in your integration is off by one register—a mistake that can go undetected for months if the adjacent register happens to return a plausible value.

Source Attribution

This guide draws on technical documentation from the following sources:

Modbus registersregister mapaddressingVFDenergy meter

Was this article helpful?

Related Articles

Need to do this remotely? SiteConduit supports Modbus remote access with the same protocol-level controls as BACnet. Join the waitlist.

SC

SiteConduit Technical Team

Idea Networks Inc.

SiteConduit builds managed remote access for building automation. Our knowledge base is maintained by BAS professionals with hands-on experience deploying and troubleshooting BACnet, Niagara, Modbus, and Facility Explorer systems.