Skip to content

Service

LoRaWAN devices, networks, and the points they become

Most of this field stops at a dashboard. A building system needs the other half: a point with one type for life, a history that adds up, and a downlink that is acknowledged — which means the device, the network server and the station all have to be somebody's problem.

  • Payload decoders
  • ChirpStack 4
  • STM32WL firmware
  • Class C downlinks
  • Niagara points

What this covers

Decoders that survive a station

A vendor's JavaScript codec is written for a dashboard, and a dashboard tolerates a key that is a number one minute and the word Error the next. A point does not: it has one type for life and a history that will not take text. We port the codec — usually into Java, sometimes into a normalising service in front of the station — keeping enum ordinals rather than English display strings, turning invalid markers into point status instead of into a value, and making missing keys a fault rather than silence. Six patterns we keep finding, with a test harness, are written up in reading a LoRaWAN payload decoder before you trust it.

The network server

We run ChirpStack 4 ourselves — server, gateway bridge, REST API and MQTT integration, in containers — so a private network on site is a configuration exercise rather than a research project: device profiles, join handling, downlink queues, retained topics that a station can subscribe to. Where a public network is already in use, the work happens against your existing tenant instead. Either way the boundary we care about is the one where a frame becomes a message somebody else has to consume.

The write direction

Reading sensors is the easy half. Controlling a device from a building system means downlinks, and that means a device that is listening: Class C, OTAA, EU868 end-node firmware on the STM32WL under FreeRTOS with ST's LoRaWAN stack, an application protocol carried on those downlinks as a register map rather than ad-hoc opcodes, and a two-MCU split where the radio MCU and the application MCU talk over a framed serial link. That is firmware we have written, and it is why a command from a station can be something other than a hope.

Proving it without the hardware

Devices arrive late and arrive in ones. A simulator that speaks to the network server as a fleet does — joins, uplinks on a schedule, downlink acknowledgement — lets the station work, the alarms and the histories be built and tested before a single unit is mounted. The same rig is how a protocol gets audited: derive the byte layout from the firmware, then run the backend against it and list every place the two disagree. We have done that and the list was not short.

What we hold to across that boundary

Each of these is a rule we adopted after watching the alternative fail somewhere real.

RuleWhy it is held
Time crosses as epoch or with an offset Decoders that format a device clock with local-time getters emit a string that reads differently on every server it runs on, and once it is in the JSON there is no recovering which zone it meant. The station has a time zone; the decoder does not need one.
One type per point, for its whole life A numeric point fed a string goes to fault or stops updating, and if the first frame after commissioning was the invalid one, the point may have been created as the wrong type and will stay that way.
Ordinals survive, display strings are extra An enum point needs the number the device sent to be alarmable, trendable and translatable. Text that arrives instead breaks the day the vendor improves their wording.
A missing value is a fault, not silence An unmapped byte that decodes to nothing leaves the key out of the payload, which downstream is indistinguishable from a message that never arrived — so the one frame telling you something new is the one that leaves no trace.
Downlinks are confirmed and re-queued A command written into a queue is not a command delivered. Class C keeps the receive window open, but the acknowledgement is still what the station should believe, and a failed write should look like a failed write.
Station-side code stays pure Java Whatever happens on the network-server side, the part installed on a controller is a signed, pure-Java module with no native library, because a JACE is an ARM/QNX box and a native dependency will not load on it at all.

Whose work this is. The device and network-server experience above comes from product engineering done under other companies' names, so there are no logos and no case studies on this page — those are theirs to publish, not ours. What is ours to describe is the part that transfers: the stack, the device class, the shape of the protocol, and the failure modes we have already paid for once. On the Niagara side the work is our own and the tools are published.

Deliverables

What you actually receive

Fixed price per deliverable, quoted against a written specification. No hourly billing, and no price before the scope is in writing.

DeliverableDetail
A written boundaryAgreed before a price exists: which side decodes, what the JSON looks like, what happens to an invalid reading, and what a failed downlink does. One page, in language both sides can hold each other to.
The decoderIn Java for the station, or as a normalising service in front of it — with the three test frames that prove it: nominal, every-invalid-marker, and one value below zero.
The network-server configurationDevice profiles, integrations and downlink behaviour, as files rather than as clicks somebody has to remember, so the same network can be stood up twice.
Firmware, where that is the jobEnd-node work on the STM32WL: join behaviour, the register map, and the downlink handling — delivered with the serial protocol written down, because that document is what outlives the developer.
A simulatorSo the station, the alarms and the histories can be built and tested before hardware arrives, and regression-tested after it does.

Also

Other services

Notes

Written up in more detail

The engineering behind this service, in public, with no pitch attached. All notes.

Next step

Send one frame and the decoder that is meant to read it.

That is enough to say whether this is a decoder job, a network-server job, or a firmware job — and which of the three you do not need. The read is free and the answer is usually a paragraph.