Modbus Register Scaling: Configuring Multiplier and Offset

Daniel Price8 min read
ModbusOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview: What Register Scaling Can and Cannot Do

Modbus slaves transmit raw 16-bit integers. Almost no field device sends engineering units directly, so the master or SCADA layer must convert counts to volts, bar, degC, or m3/h. In Modbus client/master software that exposes an Input Register Properties and Holding Register Properties dialog, that conversion is implemented as a linear transform only: a multiplier (factor) and an offset applied to the value read from the device.

The transform is:

EngineeringValue = (RawRegisterValue * Multiplier) + Offset

Anything that is not of the form y = mx + b — sine, cosine, logarithms, square root for differential-pressure flow, polynomial RTD linearization, exponentials — is not supported in the register properties dialog. Those operations must be pushed into the device, the PLC, or a downstream historian/scripting layer.

Design rule: Treat register-level scaling as a units converter, not as a math engine. If a tag requires more than one multiply and one add, solve it upstream in PLC logic where you have full IEC 61131-3 function blocks.

The Linear Scaling Math

Given a device that maps a raw count span to a known engineering span, derive the two parameters from the endpoints. Let:

  • Raw_min, Raw_max = raw register counts at the bottom and top of range
  • Eng_min, Eng_max = engineering values at those same points
Multiplier = (Eng_max - Eng_min) / (Raw_max - Raw_min)
Offset     = Eng_min - (Multiplier * Raw_min)

Verify by substituting both endpoints back into Eng = Raw*Multiplier + Offset. If both endpoints reproduce exactly, the transform is correct across the whole span because it is linear.

Worked Examples

Case Raw span Eng span Multiplier Offset
Temperature sent in 0.1 degC units -400 to 1250 -40.0 to 125.0 degC 0.1 0
Pressure, 0-10000 counts = 0-16 bar 0 to 10000 0 to 16 bar 0.0016 0
Live-zero level, 4-20 mA mapped 4000-20000 counts to 0-100 % 4000 to 20000 0 to 100 % 0.00625 -25
Kelvin register to degC 2732 to 3732 (0.1 K) 0 to 100 degC 0.1 -273.2

The live-zero case is the one engineers get wrong most often. With 4000 counts as the bottom of scale, the offset is not zero: Multiplier = (100-0)/(20000-4000) = 0.00625; Offset = 0 - (0.00625 * 4000) = -25. Enter -25, not 0, or every reading will be biased high by 25 %.

Configuration Procedure

  1. Read the device manual and record, per register: function code (Input Register = FC04, Holding Register = FC03), register address, data type, word count, and the documented engineering span.
  2. Confirm the raw value first with scaling disabled — set Multiplier = 1, Offset = 0 — and compare against a known physical stimulus or the device's own local display.
  3. Confirm data type and word order before touching scaling. A 32-bit value read with the wrong word order produces a raw number that no multiplier can rescue.
  4. Compute Multiplier and Offset from the endpoint formulas above.
  5. Open the Input Register Properties or Holding Register Properties dialog for that tag and enter the factor and offset.
  6. Set the display data type to a floating-point/real type if the multiplier is fractional. Writing a 0.0016 multiplier into a tag that is still stored as a 16-bit integer will truncate to 0 and every reading will read zero.
  7. Document the derived constants next to the tag description so the next person can audit them without re-deriving.
Writes to Holding Registers: Scaling on a writable Holding Register is a two-way transform. Before commissioning a setpoint tag, verify empirically whether your client applies the inverse transform Raw = (Value - Offset) / Multiplier on write, or writes the raw entry through unmodified. If the behavior is undocumented, write a known value and read back the raw register with a separate unscaled tag to confirm. Do not assume symmetry.

Signed, Unsigned, and Rollover Traps

Linear scaling is applied after the raw 16-bit word is interpreted, so the interpretation must be correct first.

Symptom Likely cause Fix
Negative temperatures read as ~6500 degC Signed value being interpreted as unsigned (0xFFB0 = 65456 instead of -80) Change tag data type to signed 16-bit before scaling
Value jumps between two wildly different magnitudes 32-bit value with swapped word order (big-endian vs little-endian word pairs) Correct word-order setting; scaling cannot compensate
Scaled value always 0 Fractional multiplier applied to an integer-typed tag Set the tag to a real/float type
Constant percentage error across the whole range Multiplier wrong, offset correct Recheck Raw_max/Eng_max endpoint
Constant absolute error at all values Offset wrong, multiplier correct Recheck live-zero handling
Counter tag drops to a large negative number periodically Unsigned 16-bit rollover at 65535 read as signed Use a 32-bit unsigned register pair if the device provides one; handle rollover in logic

Workarounds for Nonlinear Math

Because sine, cosine, sqrt, log, and polynomial linearization are outside the multiplier/offset model, use one of these strategies, listed in order of preference:

Strategy Where the math runs When to use it Trade-off
Configure the field device Transmitter / analyzer firmware Device already offers sqrt extraction, RTD linearization, or engineering-unit output Zero extra load; check the device manual for the setting
Compute in the PLC and expose a result register PLC scan Any custom function; full access to SQRT, SIN, COS, EXP in IEC 61131-3 Consumes PLC memory and one or two extra registers per derived value
Piecewise-linear lookup table PLC or gateway Smooth monotonic curves such as tank strapping tables Interpolation error between breakpoints; needs enough segments
Derived/calculated tag in the SCADA or historian layer Server-side expression engine Trending and reporting only, not for control or interlocks Value does not exist at the Modbus layer; other clients cannot see it
Protocol gateway with scripting Gateway CPU Legacy devices you cannot reprogram Extra hardware, extra failure point, added latency

Example: Square-Root Extraction for DP Flow

Differential-pressure flow is inherently nonlinear (flow proportional to the square root of DP), so it cannot be expressed as multiplier + offset. Compute it in the PLC and publish the result:

(* Structured Text, PLC-side derivation *)
(* DP_Raw: INT from transmitter, 0..10000 counts = 0..250 mbar *)
DP_mbar := INT_TO_REAL(DP_Raw) * 0.025;
IF DP_mbar < 2.5 THEN            (* ~1% low-flow cutoff *)
    Flow_m3h := 0.0;
ELSE
    Flow_m3h := K_Factor * SQRT(DP_mbar);
END_IF;
Flow_Reg := REAL_TO_INT(Flow_m3h * 10.0);  (* publish as 0.1 m3/h *)

The Modbus master then reads Flow_Reg as a plain Input Register with Multiplier = 0.1 and Offset = 0 — back inside the supported linear model. The low-flow cutoff is mandatory: without it, noise around zero DP is amplified by the square root and produces phantom flow totals.

Verification and Commissioning Checks

  1. Two-point check: Force the device to its low and high calibration points (loop calibrator, simulator, or physical stimulus) and confirm the scaled tag reads within the device's stated accuracy at both ends.
  2. Mid-point check: Verify a third point near 50 % of span. A correct pass at both endpoints but failure at midspan means the underlying signal is not linear and needs a PLC-side or device-side function instead.
  3. Raw shadow tag: Keep a duplicate unscaled tag (Multiplier 1, Offset 0) on the same register during commissioning. It makes it trivial to separate a scaling error from a communications or data-type error.
  4. Bad-quality handling: Confirm what the scaled tag shows on comms loss. An offset applied to a stale or zeroed raw value can present a plausible-looking engineering number (for the live-zero example, raw 0 scales to -25 %), which is why alarm logic must key on comms quality, not on value plausibility.
  5. Round-trip test for Holding Registers: Write a setpoint, read it back through the scaled tag and through the raw shadow tag, and confirm both agree with the intended value.
  6. Resolution check: Compute engineering units per count (= Multiplier). If one count exceeds the required control resolution, the register itself is the limit — no scaling change will improve it. Ask the device for a higher-resolution register or a 32-bit pair.
Rounding: Fractional multipliers on integer tags truncate. Always confirm the client stores the scaled result as a real, and never chain a second rounding stage (for example, a display format of 0 decimals) into control or totalization logic.

FAQ

How do I calculate the multiplier and offset for a Modbus register?

Multiplier = (Eng_max - Eng_min) / (Raw_max - Raw_min), and Offset = Eng_min - (Multiplier * Raw_min). For a 4000-20000 count range mapped to 0-100 %, that gives Multiplier = 0.00625 and Offset = -25.

Can I apply sine, cosine, or square root to a Modbus register value?

No. Input Register and Holding Register properties support only linear scaling with a multiplier and an offset. Nonlinear functions must be computed in the field device, in the PLC, or in a gateway/SCADA expression layer, then published as a plain register.

Why does my scaled Modbus tag always read zero?

The most common cause is a fractional multiplier applied to a tag still declared as a 16-bit integer, which truncates the result to 0. Change the tag data type to a real/float, then re-test.

Why do negative temperatures show up as a huge positive number?

The register is being interpreted as unsigned when the device sends a signed 16-bit value: 0xFFB0 reads as 65456 instead of -80. Set the tag to signed 16-bit before applying the multiplier and offset.

Does scaling on a Holding Register also apply when I write a setpoint?

Behavior varies by client, so verify it rather than assume. Write a known value, then read the same register back through an unscaled shadow tag to confirm whether the inverse transform Raw = (Value - Offset) / Multiplier was applied.

Back to blog