Building a SCADA Totalizer That Survives Counter Rollover and Meter Resets
Computing usage across a wrapping counter without inventing phantom flow: register width, correcting the delta once, and rollover versus meter reset.
The negative flow that never happened
The daily report says the raw-water meter pushed −4.29 million gallons overnight. Nothing ran backward. The meter's pulse count reached the top of a 32-bit register at 4,294,967,295, wrapped to zero, and the totalizer did new - old and got a number the size of the whole counter, with a minus sign in front of it.
Most of the SCADA data that ends up in a report comes from counters like this: flow meter pulse counts, energy kWh accumulators, run-hour meters, product counts off a packaging line. The device holds a number that only ever climbs, and you read usage as the difference between two samples. That works right up until the count hits the top of its range and rolls back to zero — and then every total downstream is wrong until somebody notices the shape of it.
The fix is small, but it has to live in exactly one place, and it needs two facts the raw tag never carries: how wide the counter is, and whether a drop in the count means a rollover or a reset.
Know the register width
A counter wraps at the top of its data type. You cannot compute rollover correctly unless you know that ceiling.
| Width | Max value | Wraps at |
|---|---|---|
| 16-bit unsigned | 65,535 | 65,536 |
| 32-bit unsigned | 4,294,967,295 | 4,294,967,296 |
| 32-bit signed | 2,147,483,647 | into negatives, then back |
| 64-bit unsigned | ~1.8 × 10^19 | effectively never in practice |
A 16-bit counter on a busy pulse input can wrap in minutes. A 32-bit counter on the same input might wrap once a year. A 64-bit counter effectively never wraps in the life of the plant. The width is a property of the field device and how the register is mapped, and it is the single most important fact to record next to the tag.
Two traps here:
- A 32-bit counter read over Modbus arrives as two 16-bit registers. If the word order is wrong you get garbage, and it can look like a rollover every time the low word wraps. Confirm the byte and word order before you trust the value.
- Some devices expose a 32-bit internal counter but only publish the low 16 bits. You then see rollover at 65,536 even though the datasheet says 32-bit. Trust the wire, not the brochure.
Computing a delta across a wrap
The whole job of totalizer logic is to turn a wrapping raw count into a monotonic usage figure. The delta between two consecutive reads is normally new - old. When the counter wraps, new is small and old is large, so the difference is negative. The correction is to add the counter's full range back in:
delta = new - old
if delta < 0:
delta = delta + counter_range # counter_range = 65536, 4294967296, ...
That single guard handles a clean rollover: the count went from near the top to near the bottom, and adding the range recovers the true increment. Keep the guard in the calculated-tag expression or edge logic that produces the usage tag, not scattered across every report that reads it. One place, one rule.
There is a subtle assumption baked in: the counter wrapped at most once between the two reads. If your poll interval is slow enough that a fast counter can wrap twice, a single range correction is not enough and you silently lose a full range of counts each time. Size the sample rate against the maximum count rate so at most one wrap can happen per interval, or read the counter often enough that this is never in question.
Rollover versus reset
A drop in the count can mean two very different things, and the totalizer must not treat them the same:
- Rollover: the counter reached its ceiling and wrapped. The true increment is
new + range - old. Add the range. - Reset: someone cleared the meter, the device rebooted, or a maintenance action zeroed the totalizer. There is no missing range to add. The right increment is usually just
new(counting up from zero), or zero if you want to drop that interval.
Telling them apart from the numbers alone is not always possible, so use context:
- If
oldwas near the top of the range andnewis near the bottom, that pattern fits a rollover. - If
oldwas in the middle of the range andnewis near zero, that is almost certainly a reset, not a wrap. - A device-restart or communication-recovery flag is the strongest signal. If the outstation reports it rebooted, treat the first delta after recovery as a reset, not a wrap. DNP3 exposes this in its device-restart indication; many PLCs give you a first-scan or power-up bit.
Guessing wrong in either direction is a real error: treat a reset as a rollover and you inject a full range of phantom usage; treat a rollover as a reset and you lose a range of real usage. When in doubt, favor dropping the ambiguous interval over inventing a huge number, and flag it for review.
Handling bad quality and gaps
Rollover logic assumes you have a trustworthy previous value. Communication gaps break that assumption. If the link was down and you missed several reads, you do not know how many times the counter wrapped in between, so a delta across the gap is a guess.
Practical handling:
- Only compute a delta between two good-quality consecutive samples. If either the old or new sample is bad quality, do not accumulate the interval; mark it and move on. Carrying a delta across a bad-quality gap is how phantom usage gets into a total.
- Hold the last good raw count and its timestamp. When good data returns, decide from the gap length and the count rate whether a single delta is defensible or whether the interval should be marked as estimated.
- Never let the totalizer go negative. A monotonic total that suddenly decreases is a signal something upstream is wrong, and it should be caught, not published.
Where to put the totalizer
You have a choice about where accumulation lives, and it drives how robust the total is:
- In the field device / PLC: the device keeps its own accumulated total in a wide register. This is the most robust because the count never leaves the device as a fragile delta. Read the accumulated engineering-units total directly and let the historian trend it. Prefer this when the device offers it.
- In the SCADA / edge layer: you read the raw wrapping count and compute the running total yourself. This is where all the rollover, reset, and gap handling above has to live. Necessary when the device only exposes a raw counter.
- In the historian as a calculated tag: accumulate from the raw counter using the historian's own delta functions. Workable, but validate it the same way you would any calculated tag, because a wrong rollover rule here corrupts long-term totals quietly.
Wherever it lives, there should be exactly one authoritative total. Two independent accumulators reading the same counter will drift apart the first time one of them handles a wrap or a gap differently, and then nobody can say which number is right.
Commissioning checklist
- Record the counter width (16/32/64-bit) and units for every counter tag, next to the point map.
- For Modbus 32-bit counters, confirm word order so a low-word wrap is not mistaken for a rollover.
- Confirm whether the raw value is the full-width counter or a truncated low word.
- Put rollover correction in exactly one place, keyed to the correct range for that width.
- Verify the sample rate is fast enough that the counter cannot wrap more than once per interval at maximum rate.
- Wire a device-restart or first-scan signal into the totalizer so a reset is not treated as a rollover.
- Gate accumulation on good quality; do not carry a delta across a communication gap.
- Assert the running total is monotonic and never goes negative; alarm if it does.
- Force a rollover on the bench if you can (or simulate one) and confirm the total steps up smoothly with no negative spike.
- Reset the meter deliberately and confirm the total does not jump by a full range.
If you force one test before you leave site, force the wrap. Preset the counter just below its ceiling — on the real device if it lets you, or with a Modbus simulator writing the register if it doesn't — let it roll over, and watch the total step up smoothly with no dip. Everything else about a totalizer you can argue through on paper. The wrap you have to see happen once.