How to deal with faulty fuel readings without losing your mind

    How to deal with faulty fuel readings without losing your mind

    A fuel report is routine stuff. It tells you how much fuel a vehicle used and whether anything unusual happened along the way. That’s how it usually works. Now imagine opening one that says a bus refueled 12 times, drained fuel 5 times, and used just 0.35 liters over 85 km. Where would you even start?

    We sifted through three days of raw tracker data, then used Navixy IoT Logic to stop bad readings from distorting the report. The same approach could help with other sensor data that occasionally turns reports and alerts into nonsense. But let’s unpack this case step by step.

    A fuel report that doesn't add up

    A 26-bus fleet got a fuel report that raised more questions than it answered. One of the buses supposedly drove 85 km on 0.35 liters of fuel, with 12 refuels and 5 drains along the way. That is a lot of activity for one day (and none of it actually happened).

    fuel-report-excerpt.webp

    The fuel graph showed where those events came from. Several times a day, the line dropped toward zero, stayed low for a while, then jumped back up. The platform did what it was built to do: it treated every fall as fuel leaving the tank and every jump as fuel coming in, and the alerts followed.

    The math was awkwardly mathing. Fuel at the start of the day, plus refuels, minus drains, minus fuel left at the end gave 62.2 + 95.13 − 106.98 − 50 = 0.35 liters. The formula was fine. So, obviously, the readings were to blame.

    The obvious fix wasn't enough to bring fuel readings back to normal

    The customer suggested a simple fix. The dips seemed to start when the engine started, so they asked us to ignore fuel readings for 20 seconds after the ignition turned on. We put it in place, but the dips kept coming. Some lasted far longer than 20 seconds. Others stretched across a whole parking stop, long after the engine had gone quiet. Guessing had gone as far as it could, so we went to the raw data.

    What 34,000 messages revealed

    A tracker sends a steady stream of small messages: a timestamp, a position and whatever the vehicle reports. The fuel level comes from the bus's own computer, which shares it over the vehicle's internal data network, the same one that feeds the gauge on the dashboard.

    We exported three days of raw data from three buses, about 34,000 messages, and went through them message by message. Three patterns explained almost everything.

    1. Parked means no news

    With the engine off, no fresh fuel data arrives, so the tracker keeps sending the last level it received. Across 36 parking stops of one bus, the reported fuel level did not change once. Readings taken while a bus is parked carry no new information.

    2. The first minutes after a start are unreliable

    Right after the engine starts, while the vehicle's systems come online, the first fuel readings are not always real. They often claimed the tank held 0 to 6 liters, whatever was actually in it. Usually this cleared within seconds. On about one start in five it took longer, and the slowest took just over 7 minutes.

    3. A short start leaves a long dip

    Put the first two patterns together and you get the deepest dips. A driver starts the engine and switches it off less than a minute later. The last reading the tracker received was one of those unreliable start-up values. With the engine off, that value is all it has, so it repeats it for the whole stop.

    15:44:31  ignition off   fuel 24.9 L
    15:44:34  ignition on    fuel  0.0 L   engine 740 rpm
    15:45:16  ignition on    fuel  5.4 L   engine 744 rpm
    15:45:17  ignition off   fuel  5.4 L
              276 more messages over 47 minutes, every one of them 5.4 L
    16:32:33  ignition off   fuel  5.4 L
    16:32:34  ignition on    fuel 21.6 L
    16:35:56  ignition on    fuel 25.3 L   driving at 45 km/h
    

    One bus, 43 seconds of engine time, then 47 minutes of a tank that looked almost empty. These are the tracker's raw messages, exactly as it sent them.

    In the report, that one stop looks like about 20 liters taken out of the tank and about 20 liters put back 47 minutes later.

    Real refuels got scrambled too. On one bus, the refuel happened between several short engine starts, and a single refuel of about 30 liters showed up on the graph as a plunge to nearly zero followed by a climb to 62 liters.

    So, why didn't the simple fix work?

    Most fuel settings look only at the numbers. Each of them fails here in its own way.

    Approach What happens with this data
    Smooth or average the readings The noise gets blended into real values. A 47-minute stretch of 5.4 L drags the average down, so the false dip gets softer and stays.
    Ignore readings below a limit A tank that really is nearly empty gets hidden too. Noise above the limit, like the 15 to 18 liter readings we found, still gets through.
    Raise the alert threshold Fewer alerts, but the fuel graph and the consumption report stay wrong.
    Rules that know the context IoT Logic checks what a person would check. Is the engine running? How long ago did it start? Does the new value hold? Noise is dropped, and real refuels and drains stay.

    The 43-second engine run is the giveaway. To reject the 5.4 L reading, the flow needs to know what the bus was doing when the tracker received it. A rule based on the fuel number alone cannot tell a bad reading from a nearly empty tank.

    Four questions, asked of every reading

    IoT Logic sits between the trackers and everything that uses their data: maps, reports and alerts. You build a flow on a visual canvas by connecting blocks. Some blocks check a condition and others calculate a new value. Formulas inside them can look back at earlier messages and at the exact time each message was recorded on the vehicle. Every message passes through the flow, and the platform sees only what comes out the other end.

    Our flow asks each fuel reading up to four questions, in order.

    fuel-reading-decision-16x9.webp

    The flow keeps the last trusted level until a reading passes the checks.

    The fuel readings took just over 7 minutes to settle after the slowest start in the recorded data, so we set the warm-up period to 7.5 minutes. During normal driving, the level never changed by more than 3.5 liters between two messages, which also matched the threshold the customer asked for. We chose 5 minutes because no noisy reading in the recorded data held steady that long with the engine running, while the levels after real refuels and drains did.

    These checks mean a refuel or drain can take up to about 12 minutes of engine running time to show up in the report. The flow waits for the warm-up period and checks that the new level holds before passing the reading through.

    The flow writes the cleaned level back into the same field the fuel sensor already reads, so graphs, reports and alerts pick it up automatically. When first switched on, it leaves readings unchanged until it has seen one clean reading after a full warm-up. That prevents it from creating a false event if it starts processing data in the middle of a bad parking stop.

    One afternoon, before and after

    fuel-before-after-16x9.webp

    The tracker's recorded data for one afternoon, replayed through the flow. The gray line shows only where the two disagree.

    Time Tracker reported Platform sees What happened
    15:44:31 24.9 L 24.9 L Parked
    15:44:34 0.0 L 24.9 L Engine starts, and the first reading is noise
    15:45:17 5.4 L 24.9 L Engine off after 43 seconds. The tracker keeps its last reading
    16:32:34 21.6 L 24.9 L Next start, 47 minutes later. The warm-up window begins
    16:40:44 26.2 L 26.2 L Warmed up, and readings are trusted again
    16:47:23 62.5 L 26.3 L Refuel, right after a new start, so it waits
    17:00:10 62.3 L 62.3 L The new level held for 5 minutes of engine running, so it is accepted

    How we made sure it works

    Before the flow touched a real bus, it ran against virtual trackers that replayed the fleet's worst moments: the 47-minute dip, a refuel in the middle of several short starts, fuel stolen while driving, readings dropping to zero mid-trip, and switching the flow on during a bad parking stop. All nine test scenarios passed. Then it ran on a real tracker on our test bench.

    We also replayed all of the recorded data, about 34,000 messages from three buses over three days in August 2026, through the same rules and counted sudden jumps of more than 3.5 liters from one message to the next. For two of the buses, the flow told whether the engine was running from engine speed.

    fuel-jumps-before-after.webp

    Deploying it takes one file. Upload the flow, choose the vehicles and switch it on. There are no visits to the vehicles and no changes to trackers, sensors or alert rules. Turning the flow off lets the platform use the raw readings again.

    The same idea works far beyond fuel

    The underlying pattern is common: a value that can be trusted only under certain conditions. The flow in this case checked ignition state, time since the start and whether a new level held. Other troublesome readings can be checked against the conditions that make sense for them.

    • Speeding alerts caused by GPS jumps. Count a vehicle as speeding only when enough satellites are in view and the speed stays high for 10 seconds.
    • Sensors that need a warm-up. Ignore temperature or pressure readings for the first minutes after a device powers up.
    • One-off spikes. Accept a new value only after it repeats or holds for a set time.

    Those conditions can be applied before a reading reaches reports or alerts. In this case, that meant a 43-second engine run no longer looked like a fuel drain followed by a refuel, while changes that held long enough still came through.

    To build flows in IoT Logic, start with the IoT Logic documentation. If you're wondering how the approach would fit your fleet or your sensors, get in touch with our team.

    Share article