Skip to main content

Connect Coreflux to Building Automation

The BACnet connector 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 connector maps each data point to an MQTT topic using the same TAG model as every other Coreflux industrial connector.

When to use this

Use the BACnet connector 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 events — ADD 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 connector, also set IP and add at least one ADD MAPPING with tags.

All configuration options

Configuration parameters

After 3 failed read cycles in a row on the current connection, the route goes to Reconnecting. Reads and writes still queued on a replaced connection give way at once and don’t count against the new one. See Connection status.

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-connector-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

Commandable objects (analog, binary and multi-state outputs and values) keep a 16-slot priority array. The device uses the value in the highest slot that holds one (1 = highest, 16 = lowest). If every slot is empty, it falls back to RELINQUISH_DEFAULT.
  • Default: every write from the connector goes to slot 8.
  • Set it: WITH WRITE_PRIORITY <1-16> in the connector config. It applies to every write on that connector, and there is no per-tag override. A value outside 1–16 is refused when the connector is added.
  • Higher slots win: a write at 8 doesn’t change the present value while a BMS or operator holds slot 1–7.
  • Relinquish isn’t supported: the connector can’t write NULL to free its slot. Its last value stays in that slot until you overwrite it, or until another tool relinquishes it.
  • Inspect it: set a tag’s ADDRESS_TYPE to PRIORITY_ARRAY or RELINQUISH_DEFAULT to see which slot is active.
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.

String sizing

For STRING tags, WITH LENGTH is the maximum string size in bytes and defaults to 256. STRING_SIZE is accepted as an alias for the same field. Connector-level MAX_APDU limits the BACnet/IP frame size — it does not size strings. Read an object’s name property as a STRING. WITH LENGTH 64 overrides the 256-byte default to match a shorter BACnet property allocation:

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 connector 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/RoomTemp → 22.5, bacnet/bms/Humidity → 55.0, bacnet/bms/Setpoint → 21.0.

Combining with Models and Actions

A BACnet connector publishes each point to its own MQTT topic, so the rest of LoT can use that data like any other topic.
First, the connector 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 connector 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 -checkConnectorConnection BMS to $SYS/Coreflux/Command. The broker returns a JSON status for the named connector indicating whether the BACnet device was reached.

Additional resources


Next Steps

Industrial Overview

Learn the patterns shared across all OT connectors.

OPC UA Client

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