There is a small box on the radiator. A number on its display. That number is how much heat the flat used in that period — and the bill comes straight out of it.
Most of my work is getting that number out of there and into a database correctly. It sounds simple. It is not.
Three ways
The first is the eye. A technician enters the flat, looks at the display, writes it down. It works. But with 4,000 flats, four people knock on doors for a month, half the residents are out, and if one number is written down wrong nobody notices.
The second is the optical head. There is an infrared window on the meter. You clamp a magnetic reading head onto it, the head talks to the meter, and the meter hands over its records — not just the current value, but past periods, error logs, serial number and calibration data.
The third is radio. Modern meters broadcast on their own. The technician never enters the flat; a receiver collects hundreds of meters while walking past the building. The industry calls this walk-by; in a car it is drive-by, and with a fixed receiver in the building nobody has to come at all.
All three live in one application. It is called ZENNER Meter Reader, written in .NET, running on a technician’s laptop or handheld.
The world of the optical head
The optical interface sits on a very old standard and it shows. You talk over a serial port, slowly, step by step:
reader → meter : wake-up sequence
reader → meter : "who are you?"
meter → reader: manufacturer, version, supported speeds
reader → meter : "let's go faster"
reader → meter : "give me the data"
meter → reader: record block
The sneakiest part is the speed negotiation. The meter starts slow, then both sides agree on something faster. Both must switch at the same instant — if one is early and the other late, the link dies quietly and all you have is garbage bytes.
This class of fault is nearly impossible to reproduce at a desk. It works in the lab and fails in the building. The cause is usually a stale serial driver, latency in an extension cable, or a USB adapter’s own buffer. So I put raw byte logging into the application: what was sent, what came back, how many milliseconds later. When a read fails on site the technician sends me the file and the reason is visible in it.
The world of radio
The radio side is a completely different mindset. Here nobody asks anybody anything.
The meter broadcasts a short telegram at intervals and goes quiet. The receiver only listens. The meter does not know the receiver exists, and the receiver has no way to reply. Miss a telegram and you wait for the next one.
The telegram is dense. Each field is a data record: what was measured, in what unit, with what multiplier, for which period. One record’s header might say “energy, kilowatt-hours, factor 10, previous period”.
[ header ] manufacturer · serial · version · device type
[ encrypted body ]
→ decrypt
[ data records ]
record 1 : energy · 128.4 kWh · current
record 2 : energy · 96.1 kWh · previous period
record 3 : volume · 41.2 m³
record 4 : flow temp · 54 °C
record 5 : error flags · 0x00
The body is encrypted because consumption is personal data. If anyone walking past a building could read who heats how much, that would be a privacy problem. Every meter has its own key, and a reader receives that key only for meters it is authorised to read.
The licence side: whose software is it?
Here a non-technical problem appears. The reading library is under the manufacturer’s licence, so every device the application is installed on needs its own entitlement.
The answer was a licence server. On first run the application derives a hardware identity, asks the server, and receives a time-limited key which it renews before expiry. I later moved the server side into its own service — BZT Service Console.
The shared secret never reaches the client. The server signs; the application only verifies. Break that separation once and anyone who inspects the application can mint their own licences. You cannot keep a secret inside a desktop application — you can only move the decision that needs the secret somewhere else.
And decoding without hardware
The most useful feature arrived last: an admin tool that can re-decode a saved telegram with no hardware present at all.
The reason was simple. When the field reported “we cannot read this meter”, my only option was to go there. Now the technician sends the raw telegram, I decode it at my desk, and I can see whether the problem is the meter, the key, or the reader itself.
That means diagnosing a field problem from the office. For a team, that is worth many times the two days it took to write.
What I learned
In metering systems the hard part is not the mathematics, it is the uncertainty of the physical world. A cable can be loose, a battery flat, a radio telegram lost in a lift shaft, a meter fitted backwards.
Software’s job is not to ignore that uncertainty but to record it. A meter that could not be read is not “0”; it is “not read”. A system that confuses the two bills somebody wrongly.