ChirpStack runs a small JavaScript function on every uplink. It receives the raw bytes and returns a JSON object. The decoded values are then available on the event stream (MQTT/HTTP) and can be forwarded to ThingsBoard, Grafana or any application. The same codec file also holds the downlink encoder for devices you control (relays, thermostats, valves).
ChirpStack Payload Decoder: From Hex to Clean JSON
Write a ChirpStack v4 payload decoder: a codec on the device profile that turns LoRaWAN hex into clean JSON, with a worked Milesight example.
The data flow
LoRaWAN devices send compact binary payloads like 0175640367AD00046873. A payload codec turns that hex into structured JSON that your dashboard, rules and alarms can actually use. Here is how to write, deploy and test one in ChirpStack v4.
Sensor / controller
Measures or controls in the field and sends LoRaWAN uplinks.
LoRaWAN gateway
Receives the radio packets and forwards them to the server.
ChirpStack
Network server: manages sessions and decodes the payload.
ThingsBoard / Grafana
Dashboards, alarms, rules and reports.
What a payload codec does
Where the codec lives in ChirpStack v4
In ChirpStack v4 the codec sits on the Device Profile, not the device: Device Profiles, your profile, Codec, JavaScript functions. Every device using that profile inherits the decoder. This is why we model one clean profile per device type instead of pasting code per device.
The decoder function
ChirpStack v4 expects a decodeUplink(input) function. input.bytes is an array of byte values; you return { data: { ... } }. Milesight devices use a channel-based format, repeated [channel id][channel type][payload]:
function decodeUplink(input) {
var bytes = input.bytes;
var data = {};
for (var i = 0; i < bytes.length; ) {
var channel = bytes[i++];
var type = bytes[i++];
if (channel === 0x01 && type === 0x75) { // battery (%)
data.battery = bytes[i]; i += 1;
} else if (channel === 0x03 && type === 0x67) { // temperature, INT16 LE / 10
data.temperature = readInt16LE(bytes, i) / 10; i += 2;
} else if (channel === 0x04 && type === 0x68) { // humidity, UINT8 / 2
data.humidity = bytes[i] / 2; i += 1;
} else {
break; // unknown channel, stop instead of mis-parsing
}
}
return { data: data };
}
function readInt16LE(b, i) {
var v = (b[i + 1] << 8) | b[i];
return v > 0x7fff ? v - 0x10000 : v;
}
Test it before you trust it
Use Device Profiles, Codec, Test (or the device's Events tab on a live uplink). Paste a known payload and confirm the JSON:
Input (hex): 0175640367AD00046873
Output (JSON): { "battery": 100, "temperature": 17.3, "humidity": 57.5 }
If a value looks wrong, it is almost always endianness (Milesight is little-endian) or a scaling factor (temperature divided by 10, humidity by 2).
Common pitfalls
- Endianness: Milesight payloads are little-endian. Reading multi-byte values in the wrong order gives nonsense numbers.
- Scaling factors: Temperature is divided by 10, humidity by 2. A missing divisor is the most common decode bug.
- Unknown channels: Break on an unrecognised channel instead of mis-parsing the rest of the payload.
- v3 to v4: v3 uses a different function signature and exposed decoded data as
objectJSON. v4 exposes it directly asobject. Migrate codecs and converters together.
From hex to dashboard
Once the codec returns clean JSON, ChirpStack publishes it over its integration. From there it flows into your dashboard, see ChirpStack to ThingsBoard. We write, deploy and operate the codec for every device we run as part of ChirpStack managed hosting.
Devices with a ready decoder
Frequently asked questions
Let's discuss your infrastructure. Digital and on-site.
Whether it's IoT platform development, hardware selection, managed hosting for ChirpStack, ThingsBoard, Grafana or NetBird VPN, or migration from a self-hosted setup - we'll find the right solution for your use case. Book a free 30-minute consultation, no commitment required.
Your contact
Timo Wevelsiep
Founder, merkaio
15 minutes, no commitment, directly with Timo.
merkaio is an independent integrator and is not affiliated with Milesight.