Milesight EM310-UDL Payload Decoder
Decode Milesight EM310-UDL LoRaWAN payloads online: battery, ultrasonic distance and tilt position from the channel/type TLV format, plus the join-time device info frame. Includes payload structure, verified examples and a decodeUplink codec for ChirpStack v4 and The Things Stack.
Example payloads
01755C03824408040001
Battery 92 % · distance 2116 mm · position: tilt (official Milesight test vector)
01756203824408040000
Battery 98 % · distance 2116 mm · position: normal (example from the user guide)
ff0bffff0101ff166713b31056670013ff090100ff0a0100ff0f00
Join-time device info: serial number, versions and Class A (from the user guide)
Payload structure
| Bytes | Field | Type / scale | Description |
|---|---|---|---|
| 01 75 + 1 byte | Battery | uint8 → % | Battery level in percent. Example: 0x5C = 92 %. |
| 03 82 + 2 bytes | Distance | uint16 LE → mm | Little-endian. Example: bytes 44 08 = 0x0844 = 2116 mm. Readings ≤ 30 mm are clamped to 30; a value of 0 means out of range (≥ 450 cm). |
| 04 00 + 1 byte | Device position | uint8 enum | 0 = normal (offset < 20°), 1 = tilt (offset ≥ 20°). Useful to detect a knocked-over or badly mounted sensor. |
| FF xx + n bytes | Device info (join frame) | TLV block | Sent once when the device joins: protocol/hardware/software versions, serial number, LoRaWAN class, power-on event. This decoder parses them instead of failing. |
Ready-to-use codec
ChirpStack v4 and The Things Stack share the same decodeUplink(input) signature:
paste this into the device profile codec (ChirpStack) or the
payload formatter (TTN).
function decodeUplink(input) {
var b = input.bytes;
var data = {};
var i = 0;
while (i < b.length - 1) {
var ch = b[i], type = b[i + 1];
i += 2;
if (ch === 0x01 && type === 0x75) { // battery, %
data.battery = b[i]; i += 1;
} else if (ch === 0x03 && type === 0x82) { // distance, uint16 LE, mm
data.distance = b[i] | (b[i + 1] << 8); i += 2;
} else if (ch === 0x04 && type === 0x00) { // device position
data.position = b[i] === 0 ? "normal" : "tilt"; i += 1;
} else {
break; // device-info TLVs (0xFF ...) or unknown: stop safely
}
}
return { data: data };
}
About the EM310-UDL uplink
The Milesight EM310-UDL measures distance with an ultrasonic transducer and is widely used for tank level, waste-bin fill and silo monitoring. It reports on FPort 85 using Milesight’s channel/type TLV format: each field is announced by a channel byte and a type byte, followed by its value.
Details that matter when decoding it:
- Distance is little-endian. The bytes
44 08mean 0x0844 = 2116 mm, not 0x4408 = 17416. - Two special distance values. Readings at or below 30 mm are clamped to 30 (blind zone), and a distance of 0 means the target is out of range (beyond ~450 cm). Treat 0 as “no echo”, not as an empty tank.
- The join frame is different. Right after joining the network the device sends a block of device-info TLVs on channel 0xFF (serial number, versions, class). A decoder that only expects sensor TLVs will corrupt every field — this page’s decoder parses them properly.
- Position flag. Channel 0x04 reports whether the sensor is mounted level (normal) or tilted ≥ 20°, which invalidates distance readings.
References
- Milesight EM310-UDL user guide (payload chapter, pages 14–16) — resource.milesight.com
- Official decoder: github.com/Milesight-IoT/SensorDecoders,
em-series/em310-udl
More device decoders
Building your own LoRaWAN network? Get Yubox Gateway OS free, or learn hands-on in the LoRaWAN Master Training.