Skip to main content

Connect Coreflux to Building Automation

The BACnet route connects Coreflux to building automation controllers (ASHRAE 135) using the unified OT TAG system. Define the BACnet configuration, add Tags for continuous polling or COV subscriptions, and handle alarm operations.
BACnet is the dominant protocol for building management systems. If your controller supports BACnet/IP, this route maps each data point to an MQTT topic using the same TAG model as every other Coreflux industrial route.

When to use this

Use the BACnet route when you need to integrate HVAC, lighting, metering, or access-control controllers that expose BACnet/IP — read live values on a schedule, subscribe to change notifications, write setpoints from MQTT, receive intrinsic alarms, or run ad-hoc reads and writes triggered by MQTT commands.

Key capabilities

  • BACnet/IP over UDP — standard port 47808 (0xBAC0) with broadcast or direct-IP discovery
  • TAG-based polling — per-point MQTT topics with a configurable polling interval
  • Change-of-Value (COV) — push subscriptions per tag (WITH COV true) without polling
  • Event alarms — BACnet Event-Notifications to MQTT with per-tag or device-wide streams
  • Bidirectional writes — MQTT-triggered writes to analog, binary, and value objects
  • On-demand eventsADD EVENT with READ, WRITE, and ACK queries triggered by any MQTT publish
  • Operator ACK workflow — auto-registered ACK handler per alarming tag
  • Direct IP mode — connect to controllers that do not respond to Who-Is/I-Am
  • Docker and hostname support — resolve container service names before opening the BACnet socket

Quick Start

Pick the starting point that matches what you need. Each tag publishes to {SOURCE_TOPIC}/{tagName} unless the tag sets its own SOURCE_TOPIC.
Connect to a controller and read a room temperature every five seconds:

Protocol Overview

BACnet (Building Automation and Control networks) is an open protocol for building automation, standardized as ANSI/ASHRAE 135 and ISO 16484-5. It enables interoperability between devices from different manufacturers in HVAC, lighting, fire detection, access control, and energy management.

BACnet variants

Coreflux supports BACnet/IP (UDP). MS/TP parameters are parsed but the transport is not validated. Use BACnet/IP for all production deployments.

Supported controllers and platforms


Configuration

Only DEVICE_ID is strictly required. For a working route, also set IP and add at least one ADD MAPPING with tags.

All configuration options

Configuration parameters

Network setup: IP, PORT, and LOCAL_PORT

BACnet/IP is UDP. Three settings control two different endpoints — the BACnet device (remote) and the Coreflux broker (local).
WITH IP accepts hostnames and Docker service names (for example coreflux-route-bacnet, host.docker.internal). The broker resolves them to IPv4 before opening the BACnet socket.

Direct IP mode

Use when the controller does not respond to Who-Is/I-Am but accepts ReadProperty at a known UDP endpoint:

Write priority

Present-value writes use BACnet priority slots (1 = highest, 16 = lowest). If a device holds a value at a higher priority, a write at a lower priority will not override the active present value. The IOTech sandbox profile seeds AO:0 at priority 1 — use WITH WRITE_PRIORITY 1 for that test device.
If a write appears to succeed but the present value does not change, the device may be holding the value at a higher priority. Try WITH WRITE_PRIORITY 1 and check whether a BMS already owns the point.

TAG System

Mapping parameters

* You may set SOURCE_TOPIC on each tag instead of on the mapping, but one of the two is required.

Tag parameters

Object address format

ADDRESS_TYPE is the BACnet property (e.g. PRESENT_VALUE, OBJECT_NAME), not the object type.

Change-of-Value (COV)

Tags with WITH COV true subscribe to BACnet Change-of-Value notifications instead of polling. A mapping with only COV tags does not need WITH EVERY. Mixed mappings poll non-COV tags on EVERY and receive COV tags via push.
COV requires support on the device side. If you receive no change notifications, confirm the controller supports COV and that WITH COV true is set on the tag.

Alarms and Event Notifications

The broker receives BACnet Event-Notification messages from the field device and publishes them to MQTT. Alarm limits and Notification Class membership are configured on the controller — the broker only needs the device to send events to its LOCAL_DEVICE_ID.

Alarm message format

Every time the BACnet device reports an alarm or state change, the broker publishes one JSON object to the alarm topic. Subscribe to that topic to see events as they happen, or subscribe to {alarm_topic}/active for a retained snapshot of alarms that are still open. Example — a room temperature crosses its high limit (bacnet/bms/RoomTemp/alarms):
Fields to know: Common to_state_short values:

Setting Alarms on BACnet ROUTE

Both modes can coexist. When both are enabled for the same object, the event is published to both topics.
Publish every Event-Notification from the device to a single MQTT topic — useful for commissioning and catch-all monitoring:
Alarm events appear on bacnet/bms/alarms. Operator ACK via bacnet/bms/alarms/ack.
On connect, when alarms are enabled and LOCAL_DEVICE_ID is set, the broker sends a directed I-Am so Notification Class recipients learn the broker’s UDP endpoint.
Set a non-zero WITH LOCAL_DEVICE_ID before enabling alarms. Without it, Event-Notifications are unlikely to arrive because the field device does not know where to send them.

Active alarm list

In addition to live events on {alarm_topic}, the broker maintains a retained snapshot on {alarm_topic}/active. Subscribe here to build an alarm panel — late subscribers receive the current state immediately without waiting for the next transition. How the list updates: Per-tag lists usually hold 0 or 1 entry (one BACnet object per tag). Device-wide lists hold one entry per alarming object on the device. Example — device-wide active list on bacnet/bms/alarms/active:
Each object in the alarms array uses the same fields as a single live alarm event. When no alarms are active, count is 0 and alarms is [] — the broker still publishes valid retained JSON.

Acknowledging alarms

When a BACnet device raises an alarm, the broker publishes it to MQTT. To clear or acknowledge it on the device, an operator publishes to the ACK topic. The broker sends AcknowledgeAlarm to the controller and replies with a result. How it works:
  1. Subscribe to the alarm topic (live events) or {alarm_topic}/active (current alarm list).
  2. Publish your acknowledgement to {alarm_topic}/ack.
  3. Check {alarm_topic}/ack/result for success: true or false.
No additional configuration is needed for ACK — the broker registers the handler automatically when you enable device-wide alarms or per-tag WITH ALARM true.
Before expecting MQTT alarms, confirm on the BMS or engineering tool:
  1. Notification Class — recipient list includes LOCAL_DEVICE_ID
  2. Intrinsic alarming on each object — NOTIFICATION_CLASS, HIGH_LIMIT / LOW_LIMIT, EVENT_ENABLE, LIMIT_ENABLE
  3. Present value actually crosses a configured limit
COV working does not guarantee alarms — they use separate BACnet services.

On-Demand Events

Use ADD EVENT when you need a single read, write, or alarm acknowledgement triggered by an MQTT publish — instead of continuous TAG polling.

Query formats

Supported operations: READ, WRITE, and ACK (alarm acknowledgement).
Publish any message to bacnet/cmd/read and the result appears on bacnet/cmd/read/result:
The route must be connected before events run. If an event returns "success": false, check that the device is reachable and that the object and property in the query are correct.

When to use what


Examples

Reads two analog inputs and one analog value every five seconds:
Publishes to bacnet/bms/RoomTemp22.5, bacnet/bms/Humidity55.0, bacnet/bms/Setpoint21.0.

Combining with Models and Actions

A BACnet route publishes each point to its own MQTT topic, so the rest of LoT can use that data like any other topic.
First, the route publishes each point under bacnet/bms:
Then the model aggregates those topics into one object on bacnet/bms/Info:

Best Practices

The default 192.168.1.1 is rarely correct. On Docker or routed networks, broadcast Who-Is may never reach the device. Set WITH IP to the controller’s address or hostname.
If a device does not answer Who-Is/I-Am, set WITH DIRECT_IP true to connect to IP:PORT and verify with ReadProperty. Otherwise the route waits on discovery timeout every connect.
Polling at one-second intervals wastes bandwidth on BACnet/IP. Use WITH COV true for occupancy sensors, fast process variables, and alarm-related points.
The broker must announce itself as a BACnet device via directed I-Am so the field device knows where to send Event-Notifications.
If a BMS holds a point at priority 1, a write at priority 8 will not change the present value. Match priority to your application and verify on the device.
COV-only mappings do not need a polling interval. Adding WITH EVERY on a COV-only mapping adds confusion without benefit.
Per-tag alarms (WITH ALARM true) give each monitoring point its own ACK topic. Use device-wide (WITH ALARMS true) as a catch-all or for commissioning.
Include event_state_to_ack and bacnet_timestamp from the received alarm message. Wrong state or missing timestamp causes ACK to fail.

Troubleshooting

Connection check

Publish -checkRouteConnection BMS to $SYS/Coreflux/Command. The broker returns a JSON status for the named route indicating whether the BACnet device was reached.

Additional resources


Next Steps

Industrial Overview

Learn the patterns shared across all OT routes.

OPC UA

Connect to cross-platform industrial servers with OPC UA.
Last modified on July 3, 2026