Milesight UC100: Modbus RS485 to LoRaWAN Converter
Milesight UC100 Modbus-to-LoRaWAN converter: read up to 32 Modbus RTU devices over RS485, own ChirpStack decoder framework and downlink control.
- LoRaWAN
- Class A, OTAA
- Fieldbus
- RS485 Modbus RTU
- Modbus devices
- Up to 32 RTU slaves polled
- History buffer
- Up to 800 records, retransmission
- Power
- 5 V DC (USB) or 8 to 28 V DC
- Configuration
- USB + Milesight ToolBox
- Mounting
- DIN rail or wall mount
What the UC100 does
Modbus channels
Up to 32 register maps: coils, discrete, input and holding registers.
Data types
INT16, UINT16, INT32, UINT32, float and on/off, all byte orders.
History records
Stores up to 800 sets locally and retransmits after an outage.
Read-error flags
Per-channel alarm when a Modbus slave fails to respond.
Modbus transparent mode
Pass raw Modbus frames over LoRaWAN for custom registers.
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.
function decodeUplink(input) {
var bytes = input.bytes;
var data = { modbus: {}, info: {} };
for (var i = 0; i < bytes.length; ) {
var channel = bytes[i++];
var type = bytes[i++];
// Modbus channel value (ff 19): channel id (1) + data length (1)
// + data def (1) + value
if (channel === 0xff && type === 0x19) {
var chId = bytes[i++] + 1; // 1-based channel index
i++; // skip data length byte
var dataDef = bytes[i++];
var sign = (dataDef >>> 7) & 0x01; // high bit = signed
var dataType = dataDef & 0x7f; // low 7 bits = register type
var read = readModbusValue(bytes, i, dataType, sign);
data.modbus["chn" + chId] = read.value;
i += read.size;
continue;
}
// Modbus read error (ff 15): channel id (1)
if (channel === 0xff && type === 0x15) {
var errId = bytes[i++] + 1;
data.modbus["chn" + errId + "_error"] = true;
continue;
}
// Device info (join / power-on): version, SN, class, status
if (channel === 0xff) { i += deviceInfoLen(type); continue; }
// Unknown / config-specific segment: stop to avoid misreads
break;
}
return { data: data };
}
function readModbusValue(b, i, dataType, sign) {
// 0,1 coil/discrete -> 1 byte on/off; 2,3 16-bit; 4,6 32-bit;
// 5,7 float; 8-11 16-bit value carried in a 4-byte slot
if (dataType <= 1) {
return { value: b[i] ? 1 : 0, size: 1 };
}
if (dataType === 2 || dataType === 3) {
return { value: sign ? readInt16LE(b, i) : readUInt16LE(b, i), size: 2 };
}
if (dataType === 5 || dataType === 7) {
return { value: readFloatLE(b, i), size: 4 };
}
if (dataType >= 8 && dataType <= 11) {
return { value: sign ? readInt16LE(b, i) : readUInt16LE(b, i), size: 4 };
}
return { value: sign ? readInt32LE(b, i) : readUInt32LE(b, i), size: 4 };
}
function readUInt16LE(b, i) { return (b[i + 1] << 8) | b[i]; }
function readInt16LE(b, i) {
var v = readUInt16LE(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;
}
function readInt32LE(b, i) {
var v = readUInt32LE(b, i);
return v > 0x7fffffff ? v - 0x100000000 : v;
}
function readFloatLE(b, i) {
var bits = readUInt32LE(b, i);
var sign = (bits >>> 31) ? -1 : 1;
var exp = (bits >>> 23) & 0xff;
var frac = bits & 0x7fffff;
if (exp === 0) { return sign * frac * Math.pow(2, -149); }
if (exp === 0xff) { return frac ? NaN : sign * Infinity; }
return sign * (1 + frac * Math.pow(2, -23)) * Math.pow(2, exp - 127);
}
function deviceInfoLen(type) {
// 0xFF segment lengths are firmware-specific; tune per deployment
if (type === 0x16) { return 8; } // serial number
if (type === 0x09 || type === 0x0a || type === 0xff) { return 2; }
return 1;
}
Implemented from the published Milesight byte specification (Communication Protocol / User Guide).
The UC100 payload is configuration-dependent: each Modbus channel carries a channel id, a data length and a data-type byte (high bit signed, low 7 bits register type), then the value. The same data type covers coils, 16/32-bit integers and floats in any byte order. This is a framework, not a drop-in, since the register-to-channel mapping is set per deployment. As a Class A device the UC100 also accepts downlinks (after an uplink) to write registers or trigger Modbus commands. Implemented from the published Milesight byte specification.
Use cases
Industrial retrofit
Make legacy energy meters and PLCs readable over LoRaWAN without rewiring.
MoreSmart agriculture
Read soil, level and flow sensors on a remote RS485 bus.
MoreUtility metering
Collect water, gas and power meters from sites without wired backhaul.
Configuration & pitfalls
Power and class
The UC100 is a Class A device powered over USB (5 V) or an 8 to 28 V DC terminal. Plan a stable supply on the RS485 cabinet rather than battery.
RS485 wiring
Match baud rate, parity and slave addresses to the field devices, and add termination on long or fast bus runs.
Modbus poll interval
Choose a realistic polling interval for the number of registers, or uplinks queue up and drain battery on downstream nodes.
History and retransmission
The 800-record buffer fills history-mode uplinks after an outage. Dashboard rules must handle batched timestamped records, not only live values.
How merkaio supports your UC100
From sourcing to day-to-day operation, all from one partner on our own European infrastructure.
Pre-staging & provisioning
We configure the UC100, 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
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.
Decoder for ChirpStack v4. merkaio is an independent integrator and is not affiliated with Milesight.