Milesight EM320-TH: Outdoor LoRaWAN Temperature & Humidity Sensor

Milesight EM320-TH outdoor LoRaWAN temp & humidity sensor: own ChirpStack/ThingsBoard decoder, decoded example, threshold alarms, cold-chain integration.

Milesight EM320-TH
EM320-THSensor
LoRaWAN
Class A, OTAA
Band / port
EU868 / port 85
Ingress protection
IP67
Temperature
-30 to +60 °C, ±0.3 °C (-30 to 0 °C), ±0.2 °C (0 to 60 °C)
Humidity
0 to 100 % RH, ±2 % RH
Battery
Replaceable, up to 10 years
Configuration
NFC (Milesight ToolBox)
Measurements

What the EM320-TH measures

Temperature

Integrated Sensirion SHT41 element, -30 to +60 °C with 0.1 °C resolution, typical accuracy ±0.3 °C below 0 °C and ±0.2 °C from 0 to 60 °C.

Humidity

Relative humidity 0 to 100 % RH, 0.5 % RH resolution, ±2 % RH accuracy.

Battery level

Reported periodically as a percentage, with on-device history and retransmission.

History records

Stores up to 3000 timestamped records on the device for gap-free logging.

Data into your dashboard

Integration

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.

ChirpStack v4 · decodeUplink
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 (°C)
      data.temperature = readInt16LE(bytes, i) / 10; i += 2;
    } else if (channel === 0x04 && type === 0x68) {   // humidity (%RH)
      data.humidity = bytes[i] / 2; i += 1;
    } else if (channel === 0x20 && type === 0xce) {   // history record
      data.history = data.history || [];
      data.history.push({
        timestamp: readUInt32LE(bytes, i),
        temperature: readInt16LE(bytes, i + 4) / 10,
        humidity: bytes[i + 6] / 2
      });
      i += 7;
    } else {
      break;
    }
  }
  return { data: data };
}

function readInt16LE(b, i) {
  var v = (b[i + 1] << 8) | b[i];
  return v > 0x7fff ? v - 0x10000 : v;
}
function readUInt32LE(b, i) {
  return ((b[i + 3] << 24) | (b[i + 2] << 16) | (b[i + 1] << 8) | b[i]) >>> 0;
}

Implemented from the published Milesight byte specification (Communication Protocol / User Guide).

Channel format: 01 75 battery (%), 03 67 temperature (INT16 little-endian, /10), 04 68 humidity (/2). The 20 ce channel carries a stored history record (timestamp, temperature, humidity) used after a network gap. Uplinks without a battery channel are normal. For ThingsBoard the same channel logic goes into an uplink converter. Implemented from the published Milesight byte specification.

Uplink (hex)

0175640367AC00046873

Decoded JSON

{ "battery": 100, "temperature": 17.2, "humidity": 57.5 }
From the field

Configuration & pitfalls

NFC setup

Keys, reporting interval and threshold alarms are set over NFC with the Milesight ToolBox before rollout.

Threshold alarms

Sent immediately, outside the reporting interval. Dashboard rules should treat alarm uplinks as priority events.

History & retransmission

The device stores up to 3000 timestamped records and retransmits them after a gateway gap, so enable the 20 ce history channel in your decoder to keep logs gap-free.

Calibration offset

The device adds a calibration offset to the raw value. Document the offset so dashboard readings stay traceable.

Your partner

How merkaio supports your EM320-TH

From sourcing to day-to-day operation, all from one partner on our own European infrastructure.

Pre-staging & provisioning

We configure the EM320-TH, set keys, intervals and alarms, and ship it ready to deploy.

Own decoder

Payload codec for ChirpStack v4 and ThingsBoard, implemented from the Milesight specification.

Dashboard integration

Data lands in your ThingsBoard or Grafana, with alarms and reports.

Operations & monitoring

We run the LoRaWAN stack and dashboards on European infrastructure, you just use the data.

Frequently asked questions

Yes. It is a standard LoRaWAN device, no Milesight gateway or cloud required. You add the codec to the device profile and provision it via OTAA.
Yes, for both ChirpStack and ThingsBoard, implemented from the published Milesight byte specification. The same channel logic goes into a ThingsBoard uplink converter.
It is a Class A device on LoRaWAN port 85. Each uplink carries temperature and humidity; the battery level is reported periodically as a percentage.
Yes. The enclosure is rated IP67 with a food-grade PC + ABS housing and an operating range of -30 to +60 °C, so it suits outdoor, refrigerated and food environments.
It ships with a replaceable battery rated for up to 10 years, depending on the reporting interval you configure, including in low-temperature environments.
The EM320-TH stores up to 3000 timestamped records on the device and retransmits them once the link returns. Enable the history channel in the decoder so the backlog lands in your dashboard.
From the same series

Related devices

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.

Timo Wevelsiep

Your contact

Timo Wevelsiep

Founder, merkaio

15 minutes, no commitment, directly with Timo.

By submitting, you agree to our Privacy Policy.

Decoder for ChirpStack v4. merkaio is an independent integrator and is not affiliated with Milesight.