Hi all — first post here, so apologies in advance if any of this has been covered.
We recently finished a fleet management app development project for a mid-sized logistics operator (roughly 180 vehicles, mixed Teltonika and Queclink hardware) using flespi as the backend. Rather than just ask one question, I wanted to write up the decisions we made and the places we're still unsure, because most of what we found while researching was marketing material rather than implementation detail.
Happy to be told we've done any of this the hard way.
Our setup, briefly
Channels per protocol, devices registered individually, telemetry into a single stream
MQTT subscription for the live map, REST for everything historical
Calculators for trip detection, idling, and geofence dwell time
A Node backend between flespi and our mobile clients (React Native, Android and iOS)
1. MQTT vs REST polling for the live map — we went MQTT, but with a caveat**
Initially we polled /gw/devices/{selector}/messages on a short interval for the live view. It worked and it was simple, but the request volume scaled badly once the vehicle count went up, and the map felt laggy at exactly the moments dispatchers cared about.
Moving the live layer to MQTT fixed the latency, but introduced a problem we didn't anticipate: mobile clients lose connection constantly — tunnels, lifts, backgrounded apps, battery optimisation killing sockets on some Android OEM builds. Our first implementation showed stale positions with no indication they were stale.
What we settled on: MQTT for the live stream, plus a REST reconciliation call on every reconnect and on app foreground, and an explicit "last updated" timestamp on every marker so the user can see when data is old.
Question: for those running MQTT into mobile clients rather than a server — do you subscribe directly from the device, or terminate MQTT server-side and push to clients over your own socket? We went server-side (flespi → our backend → clients) mostly for credential management, but I'm not certain that was the right call and I'd like to hear the counter-argument.
2. Calculators saved us more time than anything else
This was the pleasant surprise. We had originally scoped trip detection, idle time, and geofence dwell as our own logic on top of raw messages. Moving that into flespi calculators removed a meaningful chunk of backend work and, more importantly, removed a class of bug where our aggregation and the raw data disagreed.
The thing that took us longest to understand was the interval lifecycle — specifically how intervals behave when a device goes offline mid-trip and reconnects hours later with buffered messages. Our first version produced some genuinely absurd trip durations.
Question: how are others handling backdated message floods from devices that buffer offline? We ended up adding a sanity filter downstream, but I suspect there's a cleaner way to handle this at the calculator level that we've missed.
3. Device-agnostic ingestion is the actual selling point, and it changed our scoping
The client's fleet had three hardware vendors because it had been bought in three phases. On previous fleet management app development work we'd have written and maintained a parser per protocol, which is unglamorous and never-ending.
Getting normalised JSON regardless of the device underneath meant our app layer didn't need to know or care what hardware was reporting. That's a genuine reduction in long-term maintenance cost, and it's the part I'd emphasise to anyone evaluating whether to build their own ingestion layer.
The trade-off we hit: parameter naming isn't perfectly uniform across all device types for the less common telemetry fields. Fuel level in particular needed per-vendor mapping on our side.
Question: is there an established pattern for maintaining that mapping layer, or is everyone maintaining their own lookup table? Feels like a wheel that gets reinvented a lot.
4. Offline-first on the mobile side
Drivers work in areas with poor coverage, so the driver-facing app needed to function without connectivity — proof of delivery, status updates, inspection checklists. We queue writes locally and reconcile on reconnect, with server timestamps as authoritative and device timestamps preserved for audit.
The hard part wasn't the queue, it was conflict resolution when a dispatcher updated a job while the driver was offline and both sides had changes. We landed on last-write-wins with a visible conflict flag for dispatch, which is unsatisfying but at least honest.
Question: anyone solved this more elegantly for job/task state specifically? I'd be interested in whether people push conflict resolution to the platform layer or keep it in the application.
5. What I'd do differently
Set up the calculator structure before writing any application logic. We built our aggregation first and then migrated it, which meant doing the work twice. If I were starting another build tomorrow, calculators would be step one, not step four.
Also: instrument the reconnect path early. Almost every bug that reached production was in the reconnection and reconciliation logic, not in the happy path.
Thanks in advance for any corrections — genuinely interested in where our approach is wrong, particularly on the MQTT termination question. Happy to share more detail on any of the above if it's useful to anyone doing similar work.