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.
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
- 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.
- 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.
- 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.
- Compute Multiplier and Offset from the endpoint formulas above.
- Open the
Input Register PropertiesorHolding Register Propertiesdialog for that tag and enter the factor and offset. - 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.
- Document the derived constants next to the tag description so the next person can audit them without re-deriving.
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.