Case study · Connected devices
Six repositories, four benches, one system that had to behave as one
A connected-device platform for retail and facilities: battery-powered devices on a long-range radio network, a provisioning path, remote configuration, telemetry, an operator dashboard and an internal operations app. Four separate benches touched it — radio firmware, device hardware, backend, front end — and the only question that mattered was whether the chain agreed with itself at every boundary.
- LoRaWAN
- Multi-tenant GraphQL API
- Realtime telemetry
- Payload decoding
- Operator dashboard
- Measured authorship
The systems involved
What had to talk to what
| The devices | Battery-powered sensing and signalling units, plus a gateway class of device, reporting over a long-range low-power radio network. Two firmware targets, built on the programme's own bench alongside the platform. |
|---|---|
| The network server | An open-source LoRaWAN network server, deployed and operated rather than written. It hands up raw payloads and expects something above it to know what the bytes mean. |
| The platform | A multi-tenant API with background jobs and realtime channels: device registry and provisioning, configuration push, zones and groups, live and historical analytics, and a service-desk path for the people who actually operate the estate. |
| The two front ends | A customer-facing dashboard with a floor-plan builder, and an internal operations app for the staff who commission and support devices. Different users, different failure modes, one API. |
The difficulty
What made them hard to connect
The failure mode of a system like this is not a crash. It is a device that silently disagrees with the server — a payload format changed on the device side, a unit scaled by ten, a field reordered — and a dashboard that keeps drawing a confident line through wrong numbers. Nothing in the stack raises an error for that.
A programme this wide is several benches at once — devices, radio, platform, two front ends — and the expensive problems live on the seams between them rather than inside any one of them. The network server was an upstream open-source project nobody could change, and the customer front end had contributors of its own, so every component had to be defensive about edges it did not control.
Multi-tenancy made the quiet bugs expensive: a device registered to the wrong tenant is not a rendering mistake, it is one customer looking at another customer's estate.
The work
What we built
Payload decoding as a first-class component
Roughly 2,700 lines whose entire job is turning bytes into values and refusing to guess: known formats decoded explicitly, unknown ones surfaced as unknown rather than coerced into a number that looks plausible on a chart.
A multi-tenant API with tests as its largest component
Device registry, provisioning, configuration push, jobs, realtime channels and analytics — and a quarter of everything written in it is test code, which on a system whose failure mode is silent disagreement is the whole argument.
An operations app for the people holding the pager
The internal app is not a cut-down dashboard. Commissioning, device health, configuration history and support calls are a different job from reporting, and it is built as one.
Now
What it does today
Devices are provisioned, configured and monitored through one platform; payloads are decoded with the unknown cases visible rather than smoothed over; operators see zones, groups and history, and support staff see the device rather than the chart. Whether and where it is installed is the operator's business to state, not ours — this page makes no deployment claim in either direction.
| Repositories in the system | 7, of which we authored the majority of 3 |
|---|---|
| API, ours | 147 of 171 commits — 88.1% of 104,704 lines |
| Of that API, test code | 26,692 lines — just over a quarter |
| Internal operations app, ours | 101 of 133 commits — 67.0% of the lines |
| Payload decoding, ours | about 2,700 lines |
| Customer front end, ours | 143 commits — 17.2% of the lines, a contribution |
| Device firmware and simulator | Delivered on the programme's own bench; the commit record we can read carries none of our identities, so this row is attested, not measured |
The stack
What it is made of
| Devices and radio | LoRaWAN class-A devices, two microcontroller targets, gateway hardware — firmware and boards built on the programme's own bench |
|---|---|
| Network server | Open-source LoRaWAN network server, self-hosted |
| Platform | Multi-tenant GraphQL API, background job queue, realtime channels, relational database, object storage |
| Front ends | TypeScript single-page applications — a customer dashboard with a floor-plan builder, and an internal operations app |
| Integration surface | Payload decoders, device provisioning, configuration push, webhooks |
Who did what
Stated as a table above rather than a footnote, because on this system the division of labour is the story. The API, the operations app and the payload decoding are ours by a measured majority — the figures are git log --all counts, not estimates. The radio network server is an upstream open-source project we deployed and operated rather than wrote. The customer front end was a contribution to somebody else's majority. The devices, their firmware and the simulator were delivered on the programme's own bench: the repositories we can read carry none of our commit identities, which is a fact about those repositories rather than about who did the work, so that row is attested here instead of counted. Where a number exists it is measured; where one does not, this page says so.
Check it
The evidence, not a description of it
Every link here opens the thing itself — a repository, a running demo, a note with the method in it.
Next step
Tell us the version, the hardware, and what it has to do.
You will get a written scope and a fixed price against it. If the honest answer is that you do not need us, you will get that instead.