Almost every calculation that involves how a value changes over time, whether it accumulates a total, measures a rate, or takes a derivative, contains a hidden multiplier: the amount of time that actually passed between two executions. That interval is delta-time, usually written dt, and treating it as a fixed constant when it is really variable is one of the quietest sources of drift in control code. This guide explains why dt belongs in these calculations, how to capture the real elapsed time rather than assume it, and the accumulation errors that appear when a controller's scan speeds up and slows down under load.
Delta-time (dt) in one line: Delta-time, or dt, is the actual elapsed time between two executions of a piece of logic, and it is the term that turns a rate into an amount or an amount into a rate. Any integral (rate times dt) or derivative (change divided by dt) must use the measured dt rather than an assumed fixed scan, because a PLC scan varies with load; using a constant nominal scan when the real one differs makes totals and rates drift proportionally to that difference.
Rates and totals are two sides of the same relationship: a rate is an amount divided by a time, and an amount is a rate multiplied by a time. That time is dt. To accumulate flow into a total, the controller multiplies the current flow rate by the time since it last added, so the units work out: barrels per minute times minutes gives barrels. To compute a rate of change from a level that is rising, it divides the change in level by the time over which that change happened, so feet divided by minutes gives feet per minute. In both directions the elapsed time is not optional decoration; it is the term that makes the arithmetic dimensionally correct.
The temptation is to bake dt in as a constant, because a controller is configured to run a task on a nominal period and it is easy to write the code as though that period is exactly what elapses. If the task is meant to run every one hundred milliseconds, a programmer may simply multiply by a tenth of a second every scan. That works only to the extent the real interval equals the nominal one, and on a real controller it does not always, because the scan is affected by everything else the CPU is doing.
The correct mental model is that the nominal scan is a target, not a guarantee. The safe pattern is to measure how much time really passed and use that measured value, so the calculation stays right whether the scan ran on time, ran long because logic load spiked, or was delayed by communications. When dt is measured rather than assumed, a totalizer accumulates the true amount and a rate reflects the true speed of change, independent of how steady the scan happens to be.
There are two common ways to obtain a real dt. The first is a free-running clock or high-resolution timer that the controller exposes as a continuously counting value. Each execution, the code reads the current clock, subtracts the clock value it saved last time, and the difference is dt; then it saves the current value for next time. The subtlety is handling the counter rolling over when it reaches its maximum and wraps to zero, which unsigned arithmetic usually resolves cleanly if the counter width and the subtraction are treated consistently, but which will produce a wild dt if ignored.
The second way is a timestamp difference. If each sample carries a timestamp, whether stamped by the PLC, the RTU, or a device, dt is simply the difference between this sample's timestamp and the previous one. This is the natural approach when the calculation runs on data that arrives with times attached, such as historized points or telemetry, rather than on a controller executing a periodic task. It has the advantage that it reflects the time the measurement actually represents, not the time the code happened to run, which matters when data is buffered or delayed in transit.
Whichever source is used, a few guards keep it honest. A dt of zero, which happens if the calculation runs twice within one clock tick or on two samples with the same timestamp, must not be used as a divisor, so derivative code has to protect against it. An implausibly large dt, from a long stall, a restart, or a gap in telemetry, should usually be clamped or the sample skipped rather than allowed to dump a huge slug into a totalizer or produce an enormous computed rate. And the first execution after startup has no previous value to subtract, so it typically seeds the saved value and produces no output until the second pass.
The failure mode of assuming a constant scan is a slow, systematic drift rather than an obvious fault. If the code multiplies by a nominal one hundred milliseconds every scan but the controller under load actually averages a bit more between executions, a totalizer accumulates slightly too little every time, and over a shift or a month those small shortfalls add up to a total that is consistently off in one direction. Because each individual scan looks reasonable and the total still climbs sensibly, the error hides in plain sight, exactly like a mis-scaled reading that trends correctly but reports the wrong number.
Derivatives suffer the mirror problem. A rate of change computed by dividing by an assumed dt that is shorter than the real interval reads high, and one using a dt that is longer reads low, so a rate-of-change alarm can trip early or late for no reason visible in the raw signal. Because derivatives already amplify noise, feeding them a wrong dt compounds an already delicate calculation. Using the measured interval keeps the computed rate anchored to real time regardless of how the scan wanders.
In a cloud SCADA architecture such as Merobix, dt discipline lives at two levels. At the edge, the PLC, RTU, or flow computer that runs the primary totalizer and rate logic should use its own measured elapsed time so the numbers it reports are already correct. In the platform, any calculation done on historized or streamed data, a computed tag deriving a rate, or a total reconstructed from stored rate points, should use the difference between the actual sample timestamps rather than a nominal poll interval, because telemetry is buffered, store-and-forwarded, and irregular by nature. Carrying accurate timestamps end to end is what lets a rate or an integral computed after the fact match the physical reality it describes, and it is why timestamp integrity and dt correctness are really the same concern.
Because the real interval between executions varies with CPU load, communications, and interrupts, so the actual elapsed time drifts away from the nominal scan. Multiplying by a fixed constant makes a totalizer accumulate slightly too much or too little every scan, and those small errors pile up in one direction over hours and days. Measuring the true elapsed time each execution keeps totals and rates correct no matter how the scan wanders.
Read a free-running clock or high-resolution timer each execution, subtract the value you saved last time, and use the difference as dt, then save the current value for next time. Alternatively, if samples carry timestamps, take the difference between consecutive timestamps. Either way, guard against a zero dt before dividing, clamp or skip implausibly large gaps, and seed the stored value on the first pass so the first output is not garbage.
The scan time is the nominal period a task is configured to run at, a target the controller aims for. dt is the actual elapsed time that passed between two real executions, which can be longer or shorter than the nominal scan depending on load. Calculations should use the measured dt, not the configured scan time, because it is the real interval that makes a rate or an integral dimensionally and numerically correct.
Merobix reads your field devices into a cloud SCADA - the real thing behind these terms, live in days from any browser.