[email protected]

How-to · IoT

Milesight Sensor in ChirpStack: Set Up the Payload Decoder

Editorial note: Versions, commands and prices may change. Please verify critical steps independently before production use. This guide does not replace individual professional, legal or tax advice.

To turn the raw LoRaWAN bytes of a Milesight sensor into readable values such as temperature and humidity, you add a payload decoder in ChirpStack: grab the official JavaScript decoder file from the GitHub repo Milesight-IoT/SensorDecoders, open the matching device profile in ChirpStack, set the payload codec under the Codec tab to "JavaScript functions" and paste the decoder in. On the next uplink the decoded fields appear on the device. This guide walks through it step by step using the EM300-TH (temperature and humidity) as the example - fully self-hosted on your own ChirpStack instance, with no cloud lock-in and no per-device licences.

This guide assumes your gateway and ChirpStack are already running. If not, first set up ChirpStack and the LoRaWAN gateway. Reference version is ChirpStack v4 (currently 4.19.2, September 2026).

Why LoRaWAN payloads need decoding

LoRaWAN is a radio protocol with a tight time budget (duty cycle) and very small packets. So that a battery-powered sensor lasts for years and fits the narrow radio budget, it does not send plaintext JSON but the shortest possible byte sequence. A typical EM300-TH payload looks like this:

01 75 5A 03 67 EA 00 04 68 8F

ChirpStack uses the AppKey to decrypt the LoRaWAN security layer, but it does not know the vendor-specific format of this application data. Without a decoder the object field in the uplink event stays empty and you only see the Base64 or hex raw value. The payload codec is the translation rule: it reads the bytes according to the Milesight channel scheme (channel ID, channel type, data) and returns structured fields. For the layer model see our primer What is LoRaWAN.

Step 1: Get the official Milesight decoder

Milesight maintains all codecs centrally in the official GitHub repository Milesight-IoT/SensorDecoders (GPL-3.0 licence). The repo is organised by product series:

Series Example devices Typical values
em-series EM300-TH, EM300-MCS, EM500-CO2 temperature, humidity, CO2, level
am-series AM103, AM307, AM319 air quality (CO2, PM2.5, TVOC, lux)
vs-series VS121, VS133, VS373 people and occupancy counting
ws-series WS101, WS301, WS523 buttons, leak detection, smart plug

For the EM300-TH the decoder file lives at em-series/em300-th/em300-th-decoder.js. Each device folder also ships an encoder (for downlinks) and a *-codec.json in LoRaWAN device-repository format. Copy the content of em300-th-decoder.js to the clipboard. Important: always take the file for your exact model - the channel layout differs between series.

Step 2: Add the decoder as a payload codec in ChirpStack

In ChirpStack v4 the codec lives on the device profile, not on the individual device, so it applies to every sensor of the same type:

  1. In the web UI open Device profiles and pick your sensor's profile (or create it, see the gateway guide).
  2. Switch to the Codec tab.
  3. Set Payload codec to JavaScript functions.
  4. Paste the entire content of em300-th-decoder.js into the Codec functions field and save.

The Milesight file already ships the entry function that ChirpStack v4 expects. The decisive part is this wrapper at the top of the file:

// ChirpStack v4
function decodeUplink(input) {
    var decoded = milesightDeviceDecode(input.bytes);
    return { data: decoded };
}

ChirpStack hands the function an input object with bytes (a byte array), fPort and variables. The actual logic sits in milesightDeviceDecode(), which walks the channel/type bytes. The ChirpStack JavaScript environment is based on QuickJS (ES2020); the Node.js Buffer class is available. The same file also contains Decode(fPort, bytes) for ChirpStack v3 and Decoder(bytes, port) for The Things Network - for v4 only decodeUplink matters.

Save the profile and wait for the next uplink (EM300 sends every 10 minutes by default). Verify the result in two places:

  • Web UI: open the device, tab Events, and inspect the up event. The object field now holds the decoded values instead of just raw bytes.
  • MQTT: subscribe to the uplink topic and watch the object live:
mosquitto_sub -h SERVER-IP -t "application/+/device/+/event/up" -v

For the example payload the decoder returns this object:

{ "battery": 90, "temperature": 23.4, "humidity": 71.5 }

If you want to test without waiting for a real uplink, the Codec tab has no test input - instead run the file locally via Node.js or use the repo's own test setup. Once object is populated you can route the values over MQTT into a time-series database and a Grafana IoT dashboard.

Example: EM300-TH payload byte by byte

Milesight encodes each measurement as a block of channel ID + channel type + data (data little-endian). The EM300-TH uses three channels:

Bytes Channel ID Channel type Raw value Decoded
01 75 5A 0x01 battery 0x75 0x5A = 90 90 %
03 67 EA 00 0x03 temperature 0x67 INT16 LE 0x00EA = 234 23.4 °C
04 68 8F 0x04 humidity 0x68 0x8F = 143 71.5 %RH

That is how 01 75 5A 03 67 EA 00 04 68 8F becomes the JSON object above. Temperature is a signed 16-bit value divided by 10, humidity a single byte divided by 2. Other models use further channels - an AM319, for instance, also reports CO2, PM2.5, TVOC and illuminance. The matching decoder from the repo already knows these channels, so you never compute anything by hand.

Common pitfalls

  • object stays empty: the codec sits on the wrong device profile, or the device uses a different profile. Check the assignment.
  • Nonsense values: wrong decoder file (wrong model) or a failed join. With a wrong AppKey the application data stays encrypted.
  • Decoder does not refresh: after pasting, save the profile and wait for the next uplink - existing, already stored events are not re-decoded retroactively.
  • Wrong model within a series: EM300-TH, EM300-MCS and EM300-DI share channels but differ - always use the exact model file.

Next steps

With a payload codec in place, ChirpStack delivers clean, structured measurements - the basis for dashboards, alerts and analytics, fully sovereign on your own infrastructure. We can handle setup, decoder maintenance for mixed sensor fleets and operations end to end. Learn more on our pages for Milesight sensor integration and ChirpStack & LoRaWAN Network Server, in the overview of LoRaWAN solutions and in the WZ-IT IoT section. You can book a no-obligation intro call online.

Rather have it operated?

You'd rather not run IoT yourself? WZ-IT handles setup, operations and maintenance - privacy-focused from Germany.

Enquiry

Assess an IoT platform and device integration

We connect devices, gateways, networks, data processing, and applications into an operable IoT or OT platform.

  • Straight with Timo and Robin - no sales team, no pitch
  • A professional assessment of your situation
  • Concrete next steps for infrastructure, software or AI

The conversation is free and non-binding.

I got to know Timo and Robin as dedicated and professional partners who make a lot of things possible for their clients that initially seem impossible. I felt very well advised. The services offered have thoroughly convinced me. (translated)
Andrea PawlowskiCommunications Manager, Golem.de, Germany
Timo and Robin, founders of WZ-IT

Assess an IoT platform and device integration

I got to know Timo and Robin as dedicated and professional partners who make a lot of things possible for their clients that initially seem impossible. I felt very well advised. The services offered have thoroughly convinced me. (translated)
Andrea PawlowskiCommunications Manager, Golem.de, Germany

Frequently Asked Questions

Answers to the most important questions

Companies worldwide trust WZ-IT

  • Stadtwerke Brühl
  • DGHO e.V.
  • solarwerkstatt berlin
  • Krüger Dirndl
  • ABCO Water Systems
  • Golem.de
  • EVADXB
  • nextGYM
  • AInergy
  • ml&s
  • Odiseo Solutions
  • Annota
  • ARGE
  • SweetConnect GmbH
  • Aphy AG
  • CORGOS
  • Rekorder
  • SolidProof
  • Yonju
  • Keymate
  • Paritel
  • Mr. Clipart
  • Millenium
  • Negosh
  • Führerscheinmacher
  • BTI
  • Deutsche Gesellschaft für Datenschutz
Read client reviews