LoRaWAN and BACnet solve different problems. LoRaWAN is a low-power radio protocol: a sensor wakes up, sends a few compact bytes, and sleeps. BACnet/IP is a wired building-automation protocol that runs over Ethernet and expects devices it can discover and poll continuously. There is no direct mapping between the two, so something has to sit in the middle, subscribe to the LoRaWAN side, and present a BACnet face to the BMS. That something is the bridge, and being honest about it up front avoids the common expectation that a sensor "just appears" in the building management system.
LoRaWAN to BACnet: Bridge Wireless Sensors into a BMS
Bridge LoRaWAN sensors into a BACnet/IP BMS: middleware subscribes to ChirpStack MQTT, decodes payloads and exposes BACnet objects the building system can poll.
The data flow
A building management system speaks BACnet/IP, a LoRaWAN sensor does not. A middleware bridge sits between them: it subscribes to the ChirpStack MQTT stream, reads the decoded values and exposes them as BACnet objects the BMS can poll. This guide shows the honest path and a working Node-RED bridge.
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.
Why a bridge is unavoidable
The data flow
The chain has four stages: the sensor sends an uplink, a gateway forwards it to ChirpStack, ChirpStack decodes the payload and publishes it over MQTT, and the bridge turns the decoded values into BACnet objects. The prerequisite for the bridge to do anything useful is a working ChirpStack payload decoder on the device profile, because the bridge reads the clean object field, not the raw hex.
Step 1: ChirpStack MQTT stream
In the ChirpStack application under Integrations, enable the MQTT integration with the marshaler set to JSON. Every decoded uplink is then published to application/{ApplicationID}/device/{DevEUI}/event/up, with the decoded values in the object field:
{
"deviceInfo": {
"deviceProfileName": "AM103",
"deviceName": "office-201",
"devEui": "24e124710c123456"
},
"object": { "temperature": 21.4, "humidity": 48, "co2": 720 }
}
Step 2: the Node-RED bridge
The most transparent bridge is Node-RED. It subscribes to the ChirpStack MQTT topic, reads msg.payload.object, and pushes each value into a BACnet object. A small flow looks like this:
[ mqtt in: application/+/device/+/event/up ]
│
▼
[ json ] → parse the ChirpStack event
│
▼
[ function: map to BACnet points ]
│
▼
[ bacnet out ] → write Analog/Binary/Multi-state values
The function node turns the decoded object into the points the BMS will read. The instance number is derived from the device so each sensor occupies a stable, non-overlapping block of object instances:
// msg.payload is the decoded ChirpStack event
var obj = msg.payload.object;
var devEui = msg.payload.deviceInfo.devEui;
// stable per-device offset (e.g. from a lookup table)
var base = deviceInstance[devEui] || 0; // e.g. 100, 200, 300...
var points = [
{ type: "analogValue", instance: base + 0, value: obj.temperature, name: "temperature" },
{ type: "analogValue", instance: base + 1, value: obj.humidity, name: "humidity" },
{ type: "analogValue", instance: base + 2, value: obj.co2, name: "co2" }
];
// drop undefined values so the BMS never reads a stale point
msg.payload = points.filter(function (p) { return p.value !== undefined; });
return msg;
The open-source LoRaBAC flow follows the same idea but with a JSON configuration that maps each payload field to a BACnet object and derives the instance number per device. It acts as a BACnet client and writes into an existing controller on each uplink, which keeps the BACnet traffic low. A gateway-resident bridge (for example Node-RED on a Milesight UG65) usually does the opposite: it runs a small BACnet server so the BMS discovers it as a native device and polls it over BACnet/IP.
Step 3: object mapping that the BMS understands
A BMS expects standard object types, so the mapping matters more than it looks:
- Analog Value / Analog Input for numeric readings: temperature, humidity, CO2, level, power.
- Binary Value / Binary Input for on/off and contact states: door open, occupancy, leak detected.
- Multi-state Value for discrete modes: fan low/medium/high, valve open/closed/auto.
Give each device a fixed instance block so points stay stable when you add or rename sensors. The integrator binds those instances to graphics, trends and alarms in the BMS.
Downlink: commands from the BMS
Bridging is not strictly read-only. A BACnet write (a new setpoint on a WT102 thermostatic valve, for example) can become a LoRaWAN downlink. The catch is LoRaWAN timing: a Class A device only receives in the short window after its next uplink, while a Class C device receives in near real time. Schedules, setpoints and occupancy modes work well, but the BMS cannot expect instant actuation across a battery-powered fleet. The bridge enqueues the downlink on application/{ApplicationID}/device/{DevEUI}/command/down.
Pitfalls from the field
- Decoder first: The bridge reads the decoded
objectfield. With no working codec on the device profile there is nothing to map, so the decoder is a hard prerequisite. - Instance collisions: Reusing the same BACnet instance for two devices makes the BMS overwrite points. Give every device a fixed block.
- Topic template: The ChirpStack v4 application event topic is
application/{ApplicationID}/device/{DevEUI}/event/upwith no region prefix. Theeu868/style prefix applies to the gateway-side topics, not these application events. If you set a customevent_topictemplate in the MQTT integration config, the bridge subscription has to match it exactly or nothing arrives. - Stale points: A sensor that misses uplinks should not silently hold an old value. Use a reliability flag or a timeout so the BMS can tell a point is out of date.
- Server vs client: Decide early whether the bridge is a BACnet server the BMS polls or a client that writes into an existing controller. The two need different network and firewall setups.
How merkaio runs this for you
We run ChirpStack and the BACnet bridge as one managed stack on European infrastructure: the decoder on the device profile, the MQTT integration, the bridge middleware and the object mapping. The BMS integrator receives a clean BACnet device with documented, stable points instead of a pile of radio payloads. Talk to us about ChirpStack managed hosting with the bridge included.
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.