Back to Blog|Tutorials

Payload Decoding in IoT Systems: ChirpStack & Milesight VS373

January 24, 2026
Timo WevelsiepTimo Wevelsiep

In IoT systems, sensors typically send their data in compact, binary form – as byte sequences that are not directly understandable to humans or many applications. Payload decoding means converting this raw data into meaningful values.

In this article, we explain why decoding is necessary, how it works technically, and show a comprehensive practical example with ChirpStack and the Milesight VS373 fall detection sensor.

Table of Contents

1) What is Payload Decoding and Why Does it Matter?

With LoRaWAN and other LPWAN technologies, every bit counts. Radio bandwidth is limited, so manufacturers encode measurements and status information efficiently in bytes rather than sending clear-text JSON or text data.

Without decoding, downstream systems would only receive cryptic hex or Base64 strings from which no direct information can be extracted. Only decoding translates the raw sensor data into understandable, usable information – e.g., temperature values in degrees Celsius or alarm messages like "motion detected".

A simple example:

A temperature sensor might send the value 23.5 °C as byte 0xEB. Without context, EB (235 decimal) is meaningless. A decoder, however, knows that it must divide this value by 10 to get 23.5 °C.

This conversion is essential for making device data usable for users and downstream platforms.

2) How Does Payload Decoding Work Technically?

In practice, decoding is often implemented through small decoder functions, typically in JavaScript. Many IoT network servers or cloud platforms (such as The Things Network, ChirpStack, Datacake, etc.) offer the ability to execute custom code whenever a device data packet arrives.

This function reads the byte array and produces a structured JSON object with named fields. The JSON then contains, for example, "temperature": 23.5 instead of a raw hex string.

The Channel/Type Concept

Many manufacturers – including Milesight – use a channel/type concept: Each data element in the payload begins with one byte for the channel and one byte for the data type. This is followed by several data bytes depending on the type.

Example Decoder in JavaScript

// Example decoder function
function Decoder(bytes, port) {
  var decoded = {};

  if (port === 1) {
    // Temperature is encoded as integer in hundredths of °C:
    var rawTemp = (bytes[0] << 8) | bytes[1]; // Combine two bytes to 16-bit int
    decoded.temperature = rawTemp / 100.0;    // Convert to degrees Celsius
  }

  return decoded;
}

In this example, a payload 0x09 0x3B (hex) would be interpreted by the decoder as follows: The two bytes together yield the value 0x093B (2363 decimal). Dividing by 100 results in 23.63 °C. The decoder result would then be:

{ "temperature": 23.63 }

Typically, the device manufacturer provides the necessary information: which bytes represent which measurements, what conversion factors apply, or which bits represent individual status flags. Often, ready-made example decoders exist – for instance, Milesight maintains a public repository with payload codecs for all its sensors.

3) Payload Decoding with ChirpStack

ChirpStack is an open-source LoRaWAN network server that directly supports integrated decoding. For each device (or device type), you can store a codec function in the Device Profile.

Codec Configuration

Through the web interface, you'll find a "Codec" tab in the device profile where you can either choose predefined codecs or enter your own JavaScript functions.

ChirpStack offers Cayenne LPP as a standard codec – selecting this will automatically decode payloads according to the Cayenne Low Power Protocol. For proprietary formats (like most Milesight devices), select "Custom JavaScript functions" and insert the decoder code there.

ChirpStack V3 vs. V4

When integrating Milesight example decoders, note a small difference:

  • ChirpStack V3 expected a function with the header function Decode(fPort, bytes)
  • ChirpStack V4 has been unified with The Things Network – here a function decodeUplink(input) is typically used that returns an object

The principle remains the same: you adjust the function name and header if necessary, while the decoder content (byte processing) can be taken from the manufacturer's repository.

Verification in the Event Log

Once the decoder function is stored and saved in the device profile, ChirpStack automatically applies it to every received packet from the device. In the Live Event Log of the ChirpStack console, you can verify if decoding works – the decoded data appears there under the key object in the JSON.

Important: The raw payload is still preserved and stored together with the decoded values. No information is lost.

4) The Milesight VS373 – How it Works and Data Format

As a practical example, let's look at the Milesight VS373, an IoT sensor with complex payloads.

What is the VS373?

The VS373 is a radar fall detection sensor that uses 60 GHz millimeter-wave radar to detect falls. It is primarily used in elderly care, smart home, and healthcare environments to immediately detect falls or emergencies and trigger alarms.

Thanks to AI-based point cloud analysis, the sensor can detect falls with up to 99% accuracy – contactlessly and anonymously, as no camera is used.

Detected Events and States

The VS373 can do much more than just generate fall alarms:

Event Description
Motion/Presence Detection Detects whether a person is present (Occupied) or absent (Vacant). Up to 6 sub-regions can be defined.
Fall Detection (Fall Alarm) Detects severe falls and immediately sends an alarm.
Motionless Alarm Alarm for unusually long motionlessness (may indicate unconsciousness).
Dwell Alarm Alarm when a person stays too long in an area.
Out-of-Bed Alarm Detects when a person leaves the bed – important in care environments.
Lying Alarm Detects that a person is lying down (possibly on the floor after a fall).
Respiratory/Vital Status Detects breathing movements, reports tachypnea (rapid breathing) or bradycardia.

VS373 Payload Structure

The VS373 uses the typical Milesight format with the channel/type concept. For example, it uses channel 0x06 together with type 0xFB to send an alarm report. This is followed by 5 bytes of value data:

Byte Meaning
Bytes 1-2 Alarm ID (0–9999, to distinguish individual alarm events)
Byte 3 Alarm type code
Byte 4 Alarm status
Byte 5 Sub-region ID (or 0xFF if not relevant)

Alarm Type Codes

Code Meaning
0x00 Fall (fall alarm)
0x01 Motionless
0x02 Dwell
0x03 Out-of-Bed
0x04 Occupied (area occupied)
0x05 Vacant (area empty)
0x06 Bradycardia (slow breathing/pulse)
0x07 Tachypnea (rapid breathing)
0x08 Lying (person lying detected)

Alarm Status Codes

Code Meaning
0x01 Alarm (newly triggered)
0x02 Resolved
0x03 Ignore
0x04 Report Respiratory Status

5) VS373 Example Payloads Decoded

Let's illustrate decoding with two concrete examples:

Example 1: Fall Alarm

Payload (Hex): 06 FB 05 00 00 01 FF

Decoded:

Field Value Explanation
Channel/Type 06 FB Alarm data record
Alarm ID 05 00 → 5 Unique ID of this alarm
Alarm Type 00 Fall
Status 01 Alarm active
Sub-region FF Not applicable

As JSON:

{
  "alarm_id": 5,
  "alarm_type": "Fall",
  "status": "Alarm",
  "zone": null
}

In a dashboard, this payload would appear as the message "Fall detected (ID 5)".

Example 2: Out-of-Bed Alarm

Payload (Hex): 06 FB 02 00 03 01 01

Decoded:

Field Value Explanation
Channel/Type 06 FB Alarm data record
Alarm ID 02 00 → 2 Unique ID of this alarm
Alarm Type 03 Out-of-Bed
Status 01 Alarm active
Sub-region 01 Zone 1 (e.g., Bed 1)

As JSON:

{
  "alarm_id": 2,
  "alarm_type": "Out-of-Bed",
  "status": "Alarm",
  "zone": 1
}

A care dashboard could display: "Alert: Person has left Bed 1".

6) Why Decoding Matters for Grafana, ThingsBoard & Co.

Once the payload is decoded by ChirpStack, the data can be passed to visualization and IoT platforms – e.g., Grafana, ThingsBoard, Node-RED, Datacake, or custom web applications.

ThingsBoard Integration Requires Decoding

ChirpStack offers direct ThingsBoard integration, but this requires that payloads are already decoded. The ChirpStack documentation states:

"Before this integration is able to write data to ThingsBoard, the uplink payloads must be decoded. The payload codec can be configured per device profile."

Without a decoder, ThingsBoard would only receive a Base64-encoded payload string that it can't use. With a decoder, it receives ready-to-use key-value pairs (e.g., alarm_type = "Fall", status = "Alarm") that it stores directly as telemetry.

Grafana and Time-Series Databases

Suppose ChirpStack writes sensor data to an InfluxDB or TimescaleDB for Grafana visualizations. With decoding, ChirpStack can write the decoded fields as separate values:

status_code=1, alarm_type="OutOfBed", zone=1

In Grafana, these fields can then be plotted or used for alerts:

  • Graph of fall events over time
  • Live alarm panel showing if an alarm is currently active
  • Notifications for critical events

Without decoding, the database would only contain a field payload_raw = "06FB0500..." – which would need complicated queries to decipher.

In short: Decoding forms the bridge between devices and IoT applications.

7) Best Practices for Decoder Development

Creating payload decoders requires care. Here are our recommendations:

Study Manufacturer Documentation

The foundation of every decoder is the official protocol description. User manuals and Communication Protocol Guides contain tables of the payload structure. These should be thoroughly understood to cover all edge cases.

Use and Adapt Example Code

Many manufacturers (Milesight, Dragino, Netvox, etc.) provide example decoder functions. It's wise not to start from scratch but to use these templates and adapt them to your own platform.

Consistent Structure for Similar Sensors

If you have multiple devices with similar payload logic, keep the decoder code consistent. Milesight sensors all use the Channel-Type format – you can reuse byte interpretation functions.

Test Extensively

Before production use, the decoder should be tested with known example data. Manufacturers often provide example payloads in documentation.

Handle Edge Cases

IoT payloads often have special cases:

  • Ambiguous flags
  • Optional fields
  • Endianness (Little vs. Big Endian)
  • Negative values (Signed vs. Unsigned)

A good decoder handles these things and logs unknown combinations.

Comment and Document the Code

Every mapping (e.g., 0x03 -> Out-of-Bed) and every conversion formula should be documented. This allows team members to extend or debug the code later.

Version Control

Treat decoder code like production software code. Use Git to track changes – this way you can trace historical versions if an update has unexpected effects.

Integrate Decoding Early

It's most effective to perform decoding directly in the LoRaWAN network server. Centralized decoding saves time and avoids errors because not every downstream system needs to implement its own parsing rules.

8) Conclusion

Payload decoding is an invisible but crucial component of modern IoT solutions. It translates the language of devices (bits and bytes) into the language of applications (numbers and meanings).

Using ChirpStack and the Milesight VS373 sensor, we've seen how important and versatile this process is. From the first moment a LoRaWAN packet arrives to the visualization in Grafana or the alarm notification in ThingsBoard – the decoder ensures that the right information arrives.

For IoT integrators, this means: You need to know both the network platform and the device itself well. But once you understand the structure, a few dozen lines of JavaScript can turn raw data into something valuable – namely transparent IoT data that delivers real value.


Bring Your IoT Project to Life

Have an idea for connected sensors? merkaio guides you from requirements analysis to ongoing operations – technology selection, architecture, implementation, and operations.

Discuss your project →


References

Frequently Asked Questions

Why is payload decoding necessary for IoT sensors?
IoT sensors typically send their data in compact, binary formats. Payload decoding translates this raw data into understandable measurements and events, so applications, dashboards, and alerts can work with it correctly.
Where does payload decoding typically take place?
Usually directly in the IoT network or application server, e.g., in ChirpStack. The data is decoded once centrally and then passed on to all downstream systems.
Does ChirpStack support custom payload decoding?
Yes. ChirpStack allows you to store JavaScript decoders per device profile. These are automatically applied to incoming uplink data.
Does Milesight provide decoders for its sensors?
Yes. Milesight provides official decoders for its sensors, including the VS373. These can be used as a foundation and adapted to your own platform as needed.
Why is correct decoding important for platforms like Grafana or ThingsBoard?
Without decoding, these platforms only receive raw payloads. Only decoded, structured data enables visualization, alerting, rules, and integrations at the application level.
Does merkaio help with creating and maintaining payload decoders?
Yes. We handle the development, customization, and maintenance of payload decoders, including testing, documentation, and integration into ChirpStack or other platforms.
Does merkaio also support complete IoT integration?
Yes. Beyond payload decoding, we support the setup and operation of complete IoT stacks – from sensors and gateways through ChirpStack to Grafana, ThingsBoard, or custom applications.
For which use cases is this type of integration particularly relevant?
Typical use cases include elderly care, building monitoring, industrial applications, agriculture, cold chain and warehouse monitoring, as well as projects in emerging markets with distributed locations.

Ready to build your IoT project?

From idea to production – we guide your IoT journey.

Requirements analysis
Architecture & technology selection
Implementation & integration
Operations & support
Payload Decoding IoT: ChirpStack, LoRaWAN, Milesight VS373 Decoder Explained | merkaio Blog