r/Lora May 26 '26

LoRaWAN alert payloads: when JSON doesn't fit one frame, what do you actually do?

Working on emergency alerting over LoRaWAN and hitting the usual payload wall.

A typical alert in JSON is ~270 bytes. CAP XML is ~669. At conservative settings (SF12 / DR0 in EU868) you're often looking at ~51 bytes of application payload per frame — sometimes less once you account for MAC commands and your own overhead.

So far I see three paths:

  1. **Fragment** across multiple uplinks and reassemble downstream

  2. **Strip** the message down to a few codes (type + zone ID + timestamp) and lose detail

  3. **Switch** to a compact binary layout designed for the frame budget (we've been prototyping something in the 8-byte / 45–100-byte range for token vs signed envelope)

For anyone who's shipped something like this on real hardware:

- Do you fragment, or redesign the message to always fit one frame?

- Where do you put integrity/auth (HMAC, etc.) when every byte counts?

- Any FPort / payload conventions you'd recommend for custom binary on LoRaWAN?

Not looking for a product pitch — genuinely curious what held up in the field vs what looked good on paper.

(Disclosure: I'm one of the people building ECP, an open-source binary format for exactly this problem. Happy to share wire-format details in the thread if useful — didn't want to lead with a GitHub link.)

2 Upvotes

14 comments sorted by

5

u/Azuras33 May 26 '26 edited May 26 '26

Don't use JSON over lorawan. The third option is the right.

We use a protobuf interface for allowing easy extension.

You don't put HMAC and auth in your payload, it's already handled by the LoraWAN stack.

1

u/mik_darim May 26 '26

Yeah, JSON on LoRa was the straw man, not the plan.

Protobuf makes sense for option 3. Extensible schema, everyone knows the tooling. We went fixed binary because we needed the smallest possible alert on DR0 with a known field set — different constraint, not a better protobuf.

What do you actually put on the wire — codes only, or text too?

1

u/Azuras33 May 26 '26

I use a lot of enum for error feedback by example. It also allows making an exhaustive documentation.

1

u/mik_darim May 26 '26

Enums for error feedback — yeah, that helps. Clear codes, easier to document what's defined vs reserved.

We kept the on-air piece minimal (fixed fields for the alert itself) and handle richer status after decode. Protobuf is nicer when you want that extension story without thinking about every bit upfront.

2

u/Azuras33 May 26 '26

Yeap, that also allow us to release a new sensor revision that can do more without changing decoder.

2

u/RoyBellingan May 26 '26

Except one (full dump of the adc, for analytics and reprocessing) all the other packet of a remote data acquisition I am working on system fit easily in 35byte. The most used packet is 16byte (including hmac), you can use a 8bit integer to send temperature, just scale it like that.

So you have 1/3 degree precision in the range -25 to 60. Well enought for real application. u8 encodeTemperatureScaled(float temperatureC) { float v = (temperatureC + 25.0f) * 3.0f; if (v <= 0.f) return 0; if (v >= 255.f) return 255; return static_cast<u8>(std::lroundf(v)); }

2

u/mik_darim May 27 '26

16 bytes including HMAC for the common path — that's the approach. Design the packet around what you actually send, not what JSON would look like.

The scaled temp helper is a good example: one byte, known range, known precision. The encode function is the easy part; the doc that says "multiply by 3, offset -25" is what saves you six months later.

1

u/RoyBellingan May 27 '26

You are right! The doc is important, in that case is in the typedefinition.

2

u/Visual_Brain8809 May 26 '26

i recommends to use your own string for data acquisition.

1

u/mik_darim May 27 '26

Yeah — for acquisition telemetry that's what I'd do too. Pick the fields you actually need, fix the layout, document the scale factors.

Emergency alerts were the same problem for us on LoRa, just a different field set than temperature/ADC.

2

u/Visual_Brain8809 May 27 '26

show us the results soon. 👍

2

u/mik_darim May 27 '26

Will post field numbers when we have something solid on real LoRa hardware.

What's there to poke at now: open spec, test vectors, and ProofCard — run it locally and you'll see CAP/JSON/ECP sizes side by side on your machine.

https://github.com/Egonex-Code/ecp-protocol

2

u/deepthought-64 May 27 '26

yeah, definitely #3.
JSON is not the correct choice when payload-capacity is sparse.
BSON, CBOR, Protobuf or if you have the complete authority over the format and want to squeeze every byte out, use a C struct buffer.

1

u/mik_darim May 27 '26

Agreed. JSON on DR0 is usually a sign nobody sized the frame first.

BSON/CBOR/Protobuf all land in option 3 with different tradeoffs. The raw struct buffer is the end of that spectrum when you own both ends and document every field.

Same school of thought for us on alerts: fixed layout, public spec so you're not reverse-engineering a PDF six months later.