Delivered remotely for a business based in Hamburg, Germany. Names withheld by agreement.
Project Overview
Industry: Downstream fuel and fleet operations — on-site fuel dispensing, bulk storage tank management and fuel cost allocation.
Type of solution: An industrial IoT platform: a device-facing backend implementing a proprietary binary protocol over multiple transports, a REST API, a web operations and reporting console, and a mobile application used at the pump.
Business context: Organisations that run vehicle fleets — construction, transport, agriculture, municipal services, mining — commonly store and dispense their own fuel. That arrangement saves money and creates a control problem. Consumption happens without supervision, tank contents are unknown between manual dips, meters drift out of calibration as they age, and the fuel eventually has to be charged back to a department, a vehicle or a job that nobody recorded at the time. The gap between fuel purchased and fuel accounted for is typically discovered long after anything can be done about it.
General users: Drivers and plant operators at the dispenser; fleet and fuel managers; inventory controllers; finance and reporting users; company administrators; resellers administering multiple client organisations; and platform administrators.
General purpose: To make every dispense an authorised, metered, attributed and reconcilable event — and to give operations a live view of tank contents, device health and consumption rather than a retrospective one.
The Business Challenge
Unattended dispensing is unaccountable dispensing. A pump that anyone can operate produces a fuel log that nobody can defend. Authorisation has to happen at the moment of dispense, at the device, or it does not happen at all.
Attribution has to be captured before the fuel flows. Odometer, engine hours, vehicle identity and job number are worth nothing if they are entered from memory at the end of a shift. They have to be a precondition of the pump turning on.
Fuel islands often have no signal. Depots, yards, quarries and remote sites are exactly the places where fuel is stored, and exactly the places where a connectivity-dependent system fails. The system had to work with no network and reconcile afterwards without creating duplicate records.
Tank contents are invisible between manual measurements. Without continuous level telemetry, tanks run dry or get topped up inefficiently, and there is no baseline against which dispensed volumes can be checked.
Instruments drift. Pulse meters and level sensors both lose accuracy over time. Without independent calibration of each, an inventory reconciliation reports a loss that is actually an instrument error — and the real losses hide inside the noise.
Three measurements of the same fuel never agree. Metered dispensing, tank level telemetry and recorded deliveries each tell a slightly different story. The value is not in forcing them to match; it is in presenting the differences clearly enough that someone can act.
Hardware fails mid-operation. A controller that loses power during a dispense leaves an open transaction and, potentially, an open valve. That is a safety issue before it is an accounting issue, and it cannot wait for someone to notice.
Fuel cost has to be sliced many ways. By vehicle, by department, by site, by operator, by job, by product, by period — and as efficiency, in distance or hours per unit of fuel. Reporting is not a reporting feature here; it is the reason the data is collected.
Our Approach
Make authorisation a precondition of physical actuation. Before any valve opens, the platform verifies the complete chain: that the operator is active and belongs to the company, that the department and site exist, that the hose and tank are active, that the product has a valid non-zero price, that the vehicle is active, that the vehicle is authorised on that hose, that the operator is authorised on that vehicle, and that the device is registered and reachable. Each check is separate and carries its own outcome, so a refused dispense always tells the operator exactly which condition failed rather than returning a generic error at a pump in the rain.
Treat pricing as part of the control path, not the reporting path. Fuel cannot be dispensed if there is no active price for that product. Costing is therefore never reconstructed retrospectively — every transaction carries the price that was in force at the moment it happened.
Build the device protocol as a first-class layer. Registration, heartbeat, configuration, valve control, mid-operation progress and completion frames are decoded through a dedicated codec with checksum verification, sequence numbering and command acknowledgement. Every frame is persisted with its raw payload alongside the decoded values, so a disputed transaction can be re-derived from exactly what the hardware sent.
Make offline a supported mode, not a degraded one. When there is no network, the mobile application connects to the dispenser controller directly over Bluetooth, runs the same command vocabulary, drives the dispense, tracks progress locally and buffers the resulting transactions — then uploads them in bulk when connectivity returns.
Calibrate the instruments independently and keep the history. Level sensors are calibrated by recording a sensor reading against a physical measurement and deriving a persistent offset. Meters are calibrated by recording indicated volume against actual measured volume and deriving a correction factor. Both are logged, so a change in reported inventory can always be separated from a change in instrumentation.
Compute volume from geometry. Tank contents are derived from measured level using the vessel's actual dimensions and shape, with configurable formats for non-standard tanks, rather than approximated from a generic table.
Watch for what did not happen. A scheduled job looks for transactions that were opened but never reported as complete, closes them, and raises an alert. Silence from a device is treated as an event in its own right.
Push everything slow off the critical path. Alarm and notification email is dispatched asynchronously, so SMTP latency can never delay the handling of a device frame or the response to a dispense command.
Store in UTC, present in the user's zone. Time zone conversion is applied declaratively at the serialisation boundary in both directions, so correctness across distributed sites is a property of the data model rather than a discipline applied endpoint by endpoint.
The Solution
Multi-level organisation model. Tenants, companies and departments, with a reseller tier able to administer multiple client organisations, and role-driven menus and permissions constructed server-side.
Physical asset registry. Sites, dispenser and hose points, storage tanks with geometry definitions, fuel products, suppliers, vehicles with odometer and engine-hour tracking, and devices with their metering constants and firmware attributes.
Device commissioning. Bulk identifier generation and activation tracking for new hardware, so devices are provisioned as a managed inventory rather than configured one at a time in the field.
Explicit authorisation graphs. Which operators may use which vehicles, which vehicles may draw from which hoses, which operators may access which tanks — all held as data, with a scheduled job that keeps blanket authorisations correct as assets are added and retired.
Controlled dispensing. Vehicle confirmation and reading capture at the pump, optional job-number entry, valve control, live progress during the dispense, and stop from either the application or the device itself.
Offline Bluetooth dispensing. Direct device control with local transaction buffering and deferred synchronisation, for sites with no usable connectivity.
Transaction ledger. Every dispense recorded with its full attribution — company, department, vehicle, hose, tank, site, product, operator, price, job number, readings, quantity and cost — plus its origin and edit history, and separate handling for on-site, off-site and manually entered records.
Tank inventory. Continuous level telemetry converted to volume, manual level entry, delivery and receipt recording with adjustments, totalizer and pump readings, refill thresholds with per-tank notification lists, and a stop-dispensing control.
Reconciliation reporting. Book-versus-measured balance, real-time balance and pump reconciliation, presenting the relationship between metered dispensing, measured inventory and recorded deliveries.
Fuel procurement. Supplier and product catalogue, orders raised against a tank with sequential numbering, delivery scheduling and templated order notifications.
Monitoring and alarms. A live device console showing status, heartbeat, battery, signal strength and decoded fault conditions, with historical logs, consumption and level dashboards, and email alerts for device faults, abnormal transactions and refill thresholds.
Calibration management. Independent level-sensor and meter calibration with full change history.
Reporting suite. Consumption and cost by date, operator, vehicle, site, site group, department and job number; cross-tabulated department-by-vehicle and operator-by-department views; fuel efficiency in distance or hours per unit; and billing summaries.
Data exchange. Spreadsheet import of vehicles, personnel, departments and transactions with downloadable templates and per-row error reporting, plus standard, customised and scheduled export configurations.
Public site and onboarding. A multi-language marketing and product site with a hardened enquiry and new-customer flow.
Key Features
Authorised dispensing at the device Fuel is released only after a complete chain of checks on operator, organisation, hose, tank, product, price, vehicle and device — each producing a specific, actionable outcome rather than a generic refusal.
Offline Bluetooth dispensing with deferred sync The mobile application drives the dispenser controller directly when there is no network, then uploads buffered transactions on reconnection — so remote sites are fully operational rather than degraded.
Checksummed binary device protocol over multiple transports Registration, configuration, control, progress and completion handled over cellular messaging, direct socket connections and Bluetooth, with acknowledgement, sequence tracking and raw payload retention for every frame.
Continuous tank level telemetry with geometric volume conversion Level readings converted to volume using each vessel's real dimensions and a calibrated offset, giving live inventory rather than periodic manual dips.
Independent meter and sensor calibration with audit history Two distinct calibration concepts, each logged, so instrumentation drift is separated from genuine inventory loss.
Inventory reconciliation across three sources Metered dispensing, measured tank level and recorded deliveries presented together with their differences, so discrepancies surface as something to investigate rather than as a year-end surprise.
Unclosed-transaction watchdog A scheduled job detects dispenses that were started but never reported complete, closes the record and raises an alert — treating device silence as an operational event.
Attribution captured before dispensing Vehicle identity, odometer or engine hours and job number are collected at the pump as a precondition of fuel flowing, so cost allocation is a by-product of the process rather than a later reconstruction.
Role-driven multi-tenant administration Tenant, company and department hierarchy with a reseller tier, and menus and actions constructed at runtime from the signed-in role's permissions.
Cost and efficiency reporting across every operational dimension Consumption and cost by vehicle, department, site, operator, job and period, with fuel efficiency and billing summaries derived from the same authoritative ledger.
Technical Architecture
Device edge. Dispenser controllers and tank level sensors communicate over cellular messaging to a broker, over a direct socket connection to a dedicated listener, or over Bluetooth to the mobile application. A shared codec decodes each frame type, verifies its checksum, updates device state, persists the raw and decoded record, returns an acknowledgement and fans the result out to live subscribers and alerting.
API layer. A token-authenticated REST API with controller trees partitioned by audience — administrative, monitoring and mobile — behind a global authentication interceptor with a narrow exclusion list, structured business error codes and generated interface documentation.
Business services layer. A service layer over a mapper-based persistence tier, with the dispense authorisation chain, the inventory and reconciliation calculations, the calibration derivations and the reporting aggregations as its principal concerns. Heavy aggregation is expressed as hand-written SQL rather than generated queries.
Real-time layer. Server-side WebSocket endpoints push device state and dispense progress to the operations console and to connected applications, keyed by channel type and user session.
Scheduling layer. Recurring jobs handle the unclosed-transaction watchdog and the synchronisation of blanket authorisation relationships as assets change.
Data layer. A relational database holding the organisational hierarchy, asset registry, authorisation graphs, transaction ledger, inventory movements, device report history and calibration logs, with logical deletion applied globally and all timestamps stored in UTC.
Caching layer. An in-memory store holds session tokens, refresh tokens, user context and external service tokens, keeping the application tier free of local session state.
Operations console. A component-based single-page application with a runtime-constructed route table driven by server-returned permissions, dense virtualised data grids for operational tables, charting for inventory and consumption, multi-language support and spreadsheet import/export flows.
Field application. A cross-platform mobile client combining a network path and a Bluetooth path over the same command vocabulary, with local transaction buffering, connectivity detection, push messaging and multi-language support.
Flow: Dispenser controllers and level sensors → binary protocol over messaging, socket or Bluetooth → codec and device state handling → authorisation, metering and inventory services → relational store → real-time push, alerting and reporting → operations console and field application
Technology Stack
| Category | Technology |
|---|---|
| Backend language | Java |
| Backend framework | Spring Boot |
| Database | MySQL with MyBatis-Plus and XML-mapped queries |
| Caching & session store | Redis |
| Device messaging | MQTT via Spring Integration |
| Device socket transport | Netty TCP server |
| Device protocol | Custom binary frame codec with checksum verification and command acknowledgement |
| Real-time web delivery | Server-side WebSocket endpoints |
| API authentication | JWT with a server-side token registry and refresh tokens |
| API documentation | Generated OpenAPI specifications |
| Reporting & data exchange | Apache POI for spreadsheet import and export |
| Templated HTML notifications over SMTP; SendGrid with SMTP fallback on the public site | |
| Observability | Log4j2 with rolling retention; automatic slow-query detection on the data source |
| Scheduling | Framework-native scheduled jobs with a configured async thread pool |
| Admin console | Vue 3 with TypeScript, Vite, Element Plus, Pinia |
| Admin data & charts | Virtualised data grid components, ECharts visualisation |
| Admin tooling | ESLint, Prettier, Stylelint, Husky, lint-staged, conventional commits |
| Mobile framework | React Native with TypeScript and React Navigation |
| Mobile device access | Bluetooth Low Energy client with a bundled protocol UUID reference set |
| Mobile services | Firebase authentication and push messaging, connectivity detection, local storage |
| Public site | Vue with a hardened Node service: captcha, per-route rate limiting, process management |
| Deployment | Capistrano release and rollback pipelines onto systemd services, with staging and production stages; container-based front-end builds |
Technical Challenges & Solutions
| Challenge | Our Approach |
|---|---|
| Fuel must not flow to an unauthorised operator, vehicle or hose | A complete pre-dispense authorisation chain evaluated as a sequence of independent checks across operator, organisation, asset, product, price, vehicle and device — each with a distinct outcome, so the person at the pump is told precisely what is wrong instead of receiving a generic failure. |
| Fuel islands frequently have no network connectivity | Bluetooth built as a first-class dispensing path rather than a fallback: the mobile application drives the controller directly using the same command vocabulary, tracks progress locally, buffers transactions and synchronises in bulk on reconnection. |
| The same device commands must work over three different transports | A shared frame codec with checksum verification, sequence numbering and acknowledgement handling, implemented once server-side for the networked transports and mirrored in the mobile client for the direct path, so behaviour is consistent regardless of how a device is reached. |
| Meters and level sensors drift, hiding real losses inside instrument error | Two independent calibration mechanisms — a level-sensor offset derived from reading versus physical measurement, and a meter correction factor derived from indicated versus actual volume — each with a full change history, so instrumentation and inventory can always be separated. |
| Metered dispensing, tank telemetry and delivery records never agree exactly | Reconciliation reporting that presents book balance, real-time balance and pump reconciliation side by side with their differences, so discrepancies become an operational signal rather than a year-end discovery. |
| Hardware can fail mid-dispense, leaving an open transaction | A short-interval watchdog that identifies dispenses started but never reported complete, corrects the record and raises an alert — treating device silence as an event rather than as missing data. |
| Fuel cost must be analysed across many independent dimensions | A fully attributed transaction ledger capturing organisation, asset, operator, vehicle, product, price and job at the moment of dispense, with aggregation pushed into hand-written SQL where the reporting shapes are heavy. |
| Notification volume must never delay device handling | Alarm, order and threshold notifications dispatched asynchronously on a dedicated thread pool, so mail delivery latency cannot block frame processing or dispense control. |
| Distributed sites operating across time zones | UTC storage with declarative conversion applied at the serialisation boundary in both directions, making time zone correctness a property of the model rather than a per-endpoint discipline. |
Security & Reliability
Token authentication with central revocation. Sessions are authenticated by signed token and validated against a server-side registry, so access can be withdrawn centrally rather than waiting for a token to expire. Refresh tokens are issued and rotated for the mobile client.
Session invalidation on credential and status change. A password change or an account deactivation invalidates existing sessions at the next request, enforced in the authentication interceptor rather than left to individual endpoints.
Authorisation as data, enforced server-side. Which operators may use which vehicles and hoses, and which tanks they may access, is held as explicit relationships. Menu structure and available actions are constructed from the signed-in role's permissions on the server, not assumed by the client.
Physical actuation behind a complete check chain. Because a dispense is a real-world side effect, every precondition is asserted before a valve is opened, and each failure is distinguishable.
Full device provenance. Every decoded device frame is retained with its raw payload, so any transaction can be re-derived from what the hardware actually transmitted — the basis for resolving a disputed dispense.
Calibration audit trail. Changes to sensor offsets and meter factors are logged with who and when, so a shift in reported inventory can always be attributed.
Failure detection rather than failure assumption. Incomplete dispenses, device faults and threshold breaches are detected and alerted, with fault conditions decoded into readable status rather than stored opaquely.
Hardened public entry points. The public-facing enquiry and onboarding forms apply captcha verification and per-route rate limiting.
Operational observability. Structured logging with size and time-based rotation and retention policies, and automatic detection of queries exceeding a fixed latency threshold.
Atomic releases with rollback. Deployment uses versioned releases with retained history and a single-command rollback path across staging and production stages.
Scalability & Performance
Stateless application tier. Session state, user context and external service tokens live in an in-memory store rather than in the application instance, so instances can be added or replaced without affecting signed-in users.
Device traffic decoupled from user traffic. Device ingestion arrives on its own transports, so a surge in telemetry does not compete with interactive requests, and broker capacity becomes an independent scaling lever.
Aggregation pushed to the database. Report queries that touch large volumes of transaction history are written as explicit SQL rather than generated by the ORM, keeping the aggregation where it belongs and the result sets bounded by pagination.
Automatic slow-query detection. The data source is instrumented so that queries exceeding a fixed threshold are surfaced by default, rather than being discovered by users.
Asynchronous side effects. Notification and alerting work is dispatched to a configured thread pool, so the latency of external mail delivery never appears in a device or user response.
Cached authentication path. Session validation resolves from the in-memory store, avoiding a database round trip on every authenticated request.
Bounded scheduled work. Recurring jobs operate over explicit query windows rather than scanning full history.
Client-side efficiency. The operations console uses virtualised grid components for dense operational tables and constructs only the routes a role can access; the mobile application polls dispense state on a short fixed interval only while a dispense is active.
Business Outcomes
- Dispensing became a controlled event. Fuel is released only against a verified operator, vehicle, hose, tank and price, and a refusal explains itself at the pump.
- Attribution is captured at the point of use. Vehicle, readings and job number are collected before fuel flows, so cost allocation is a by-product of dispensing rather than a monthly reconstruction.
- Remote sites operate at full capability. Bluetooth dispensing with deferred synchronisation means no connectivity does not mean no fuel — and no missing records.
- Tank contents are known continuously. Level telemetry converted through vessel geometry replaces periodic manual measurement, supporting both refill planning and reconciliation.
- Instrument drift is separated from inventory loss. Independent, audited calibration of meters and sensors means a reconciliation discrepancy points at something real.
- Discrepancies surface as operational signals. Metered dispensing, measured inventory and recorded deliveries are presented together with their differences, when they can still be acted on.
- Incomplete operations are caught automatically. A dispense that never reports completion is detected, closed and alerted rather than sitting open in the ledger.
- Fuel cost is analysable across every dimension the business cares about — vehicle, department, site, operator, job, product and period, including efficiency measures, all derived from one authoritative record.
- Device health is visible. Live status, connectivity, battery, signal and decoded fault conditions turn hardware maintenance into something planned rather than reactive.
Why it worked
Software that controls physical equipment is a different discipline from software that records what happened. A failed API call can be retried; a valve that opens when it should not have has already dispensed the fuel. That difference shows up everywhere in a platform like this — in how authorisation is structured, in how commands are acknowledged, in what happens when a device goes quiet mid-operation, and in whether the system is honest about the difference between what it measured and what it believes.
Our team builds for that reality. We treated the device protocol as a first-class layer with checksum verification, acknowledgement and full raw-payload retention, because when a transaction is disputed the only credible answer is what the hardware actually sent. We made offline operation a supported mode rather than a degradation, because the places fuel is stored are the places connectivity fails. We built two independent calibration mechanisms with audit history, because a reconciliation system that cannot separate instrument drift from real loss is worse than none. And we made the platform notice what did not happen — the dispense that never closed, the device that stopped reporting — because in industrial systems, silence is data.
Our teams work across Java and Spring, industrial device protocols and IoT messaging, Bluetooth and embedded integration, real-time web delivery, modern TypeScript front ends, cross-platform mobile development, and the reporting and reconciliation logic that operational businesses actually run on — with the domain judgement to know which parts of a physical process must be modelled exactly and which can be simplified.
Final Summary
A fleet operator with its own fuel storage has a control problem disguised as a convenience. Fuel is dispensed at an unattended pump, by whoever turns up, into whatever is parked there, and the record of what happened is a clipboard. Tank contents are unknown between manual measurements. Meters drift. Deliveries, dispensing and stock levels tell three different stories, and the differences are only discovered when it is far too late to investigate them.
Our team built a platform that closes that loop at the point where it opens. The dispenser is a connected, controlled device: it will not release fuel until the platform has verified the operator, their authorisation on the vehicle, the vehicle's authorisation on the hose, the tank, the product and its current price — and it captures the vehicle identity and readings as a precondition, not a formality. Every dispense is metered by the device, reported over a checksummed binary protocol, retained with its raw payload, priced at the rate in force and attributed to a department, a vehicle, an operator and a job. When there is no network, the mobile application drives the controller directly over Bluetooth and synchronises afterwards, so a remote site loses connectivity without losing either fuel or records.
Around that sits the machinery an operational business needs to trust the numbers: continuous tank level telemetry converted through vessel geometry, independently calibrated meters and sensors with audit history, reconciliation reporting that shows metered dispensing against measured inventory and recorded deliveries, a watchdog that catches dispenses no device ever finished, live device health with decoded fault conditions, and a reporting suite that slices fuel cost and efficiency every way the business is actually managed. Delivered as a Java backend speaking a multi-transport device protocol, a role-driven Vue 3 operations console and a React Native application built for the pump — it replaces a clipboard with an accountable record, which in fuel management is the whole game.