The sensor you just installed reports temperature as a 16-bit integer in tenths of a degree Fahrenheit, stored in Modbus holding register 40003. The sensor three feet away from it — different manufacturer, same application — reports temperature as a 32-bit floating point in degrees Celsius, in OPC UA node ns=2;s=TempSensor.ProcessValue. The third sensor is wireless and publishes a JSON payload over MQTT where the temperature field is labeled "tmp_f" and represents whole-degree Fahrenheit with no decimal.
All three sensors measure the same physical parameter. None of their output formats are the same. If you want a single time-series chart showing temperature across all three, you need a normalization layer. If you want a single alert rule that fires when any temperature exceeds 85°C, you need units conversion and format parsing on all three paths.
In a real facility with 300+ sensors from 15+ manufacturers, the normalization problem is not a side issue. It's the central engineering challenge of the deployment.
The Units Problem
Industrial sensors originate from manufacturers worldwide, and unit conventions vary by geography, industry, and decade. Pressure sensors from US manufacturers often output in PSI; European equivalents output in bar or kPa. Temperature sensors in process industries typically output Celsius; legacy HVAC instrumentation often uses Fahrenheit. Flow meters might output in gallons per minute, liters per second, cubic meters per hour, or standard cubic feet per minute depending on the manufacturer and the configuration set in 1998.
The units problem gets worse with derived quantities. Vibration sensors output acceleration in g (multiples of 9.81 m/s²), in m/s², in mm/s RMS, or in inches per second peak, depending on the measurement standard the manufacturer targeted. A vibration reading of 0.45 in/s peak looks very different from 11.4 mm/s RMS but they're the same physical signal under different representations.
Without a systematic normalization layer, a mixed-unit deployment produces time series that can't be directly compared or alerted on with shared rules. You end up with alert thresholds like "PSI > 87" on some sensors and "bar > 6" on others, and any analyst looking at cross-sensor data has to hold the unit mapping in their head.
Timestamp Fragmentation
Timestamps sound like a solved problem. They're not. In a mixed-protocol industrial environment, timestamps arrive in at minimum six different formats: Unix epoch seconds (integer), Unix epoch milliseconds (integer), ISO 8601 with timezone offset, ISO 8601 without timezone (ambiguous), Windows FILETIME (100-nanosecond intervals since January 1, 1601), and PLC-specific formats like the Siemens DATE_AND_TIME format (BCD-encoded, 8 bytes).
Some devices don't include a timestamp at all — the receiving system applies its own timestamp at ingestion. This works when the latency from sensor to ingest is consistent and small. It fails when a Modbus poller is backlogged, when a wireless sensor retransmits a buffered packet from two hours ago, or when a gateway buffers data during a WAN outage and uploads it all at once when connectivity is restored. You end up with accurate data assigned the wrong timestamp, which corrupts trend analysis and makes anomaly detection unreliable.
The right architecture applies sensor-side timestamps where the hardware supports them, uses the gateway's local clock as a fallback, and preserves both the original hardware timestamp and the ingestion timestamp so the difference can be audited later. For devices that don't provide timestamps at all — many legacy Modbus instruments — the poller timestamp is the best available approximation, and the polling interval sets the minimum time resolution of the data.
Register Scaling and Offset Quirks
Modbus devices typically scale their output to fit within 16-bit integer registers. A pressure sensor with a 0-1000 PSI range might output 0-10000 in a Modbus register (scale factor 0.1 PSI/count) to preserve one decimal place of precision. Another manufacturer's sensor with the same range might output 0-32767 (scale factor 0.0305 PSI/count, a consequence of maximizing the available integer range). A third might add an offset — outputting 4000-20000 counts to map to the 4-20mA current output, requiring the formula: PSI = (register - 4000) / 16000 * 1000.
These scaling parameters are buried in the device's register map documentation, which may or may not be in English, may or may not be accurate for the specific firmware version installed on the device, and is often stored nowhere accessible to the integration team. We've documented deployments where engineers spent two days cross-referencing register map PDFs from different hardware versions to figure out why a temperature reading was showing 4.26°C instead of 42.6°C. The answer was that the scale factor in the integration code was 0.01 rather than 0.1, and the original error had been carried forward through three firmware generations.
Tag Naming and Asset Context
Even after units, timestamps, and scaling are resolved, you have a metadata problem: what does this tag mean in operational context? A tag named "PV_01_TEMP" tells you it's a process value temperature reading from sensor 01. It doesn't tell you which machine, which bearing, which process line, or which building. Without that context, the data is hard to query and impossible to correlate with maintenance records.
The industry has moved toward hierarchical tag naming — ISA-95 and ISA-88 define asset hierarchy models (Enterprise > Site > Area > Line > Unit > Sensor) that provide a consistent way to express context. OPC UA's address space model natively supports this hierarchy. Modbus and MQTT require it to be added as metadata on the integration side.
Building and maintaining this tag hierarchy is tedious work, but it's the foundation on which cross-sensor analytics and maintenance correlation are built. A deployment that invests in asset context during integration pays for it many times over when analysts can write queries like "show all temperature sensors in Building 3, sorted by distance from average" rather than "show tag PV_01_TEMP and PV_17_TEMP and PV_23_TEMP."
Doing This at Scale Without Custom Code
A deployment with 400 sensors from 30 manufacturers has, conservatively, 50-100 distinct normalization rules: scale factors, offset corrections, unit conversions, timestamp format parsers, tag name mappings. Building and maintaining these in custom code means maintaining a custom codebase indefinitely — every new sensor added requires a code change and a deployment.
The alternative is a configuration-driven normalization layer where each sensor type has a driver specification (how to read from it) and a normalization profile (how to convert its output to canonical units and format). New sensor types require a new profile, not a code change. Existing profiles can be updated in configuration without touching the integration code. This is a significant operational difference at scale.
Dealing with a mixed-sensor, mixed-unit deployment?
SensorVault ships with normalization profiles for 200+ sensor types across all major industrial protocols. Add a sensor type; we handle the translation.
See the Platform