Skip to main content

BACnet

Pull

Synopsis​

Director reads one BACnet device over BACnet/IP (ASHRAE 135 Annex J, UDP port 47808): a controller of a building management system, an air handling unit, a chiller, a boiler, a VAV box, an energy meter, a fire or an access panel, or the front end of a BMS such as Johnson Controls Metasys, Siemens Desigo, Honeywell, Schneider, or Tridium Niagara. Nothing is installed on the device or beside it: Director speaks BACnet as an operator workstation does, and writes what it reads as ECS records.

It only reads. It never sends a WriteProperty or a WritePropertyMultiple, never commands, restarts, time-synchronizes, or silences a device, and creates or deletes no object. What it sends is Who-Is, ReadProperty, ReadPropertyMultiple, SubscribeCOV (and its cancellation), the answers a client owes, and Register-Foreign-Device. What it leaves at the far end is a subscription to an object's changes of value, which it cancels when it stops, and its registration with a BBMD as a foreign device, which it lets run out. See What It Sends.

One device, one BACnet device. Monitoring forty controllers takes forty devices. What a device reads is declared by its datasets: a BACnet device's objects have identifiers, not meanings, so a table of objects is your own, and the inventory dataset finds what there is to declare.

Unlike the other polled devices, it binds a port. BACnet/IP's devices answer to UDP port 47808 on both sides, so the BACnet devices of one Director share one socket, and any number of them can use 47808. A device of any other type on that port is refused, and the other way round. See One Socket per Director.

The device runs on Director only; no agent runs it. One Director owns each device: two would read it twice, and take two of the few subscriptions a controller has room for.

note

The BACnet device needs Director 2.4.1 or later. With no Director or cluster at that version selected, the wizard shows BACnet devices are unavailable on directors or clusters below version 2.4.1 and the device cannot be created.

Schema​

- id: <numeric>
name: <string>
description: <string>
type: bacnet
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
port: <numeric>
device_port: <numeric>
device_instance: <numeric>
network_number: <numeric>
mac_address: <string|numeric>
broadcast_address: <string>
bbmd_address: <string>
bbmd_port: <numeric>
bbmd_ttl: <numeric>
bind_address: <string>
timeout: <numeric>
max_retries: <numeric>
segmentation: <boolean>
max_segments: <numeric>
max_apdu_length: <numeric>
definitions: <definition[]>

Configuration​

The following fields are used to define the device:

Device​

FieldRequiredDefaultDescription
idY-Unique numeric identifier
nameY-Device name
descriptionN-Optional description
typeY-Must be bacnet
tagsN-Optional tags
pipelinesN-Optional pre-processor pipelines
statusNtrueEnable/disable the device

The Device Read​

The BACnet device is named by its address, by its instance, or by both. One of address and device_instance is required.

FieldRequiredDefaultDescription
addressN*-Host name or IPv4 address of the BACnet device, or of the BACnet router it sits behind. A wildcard, a broadcast or a multicast address, an IPv6 address, and a port written into the address are refused: BACnet/IP is IPv4's, and the port is its own field.
device_portNportThe UDP port the device at address is reached on, when it is not the one Director binds, from 1 to 65535. Not used, and the device's log says so, for a device found by its instance.
device_instanceN*-The device's instance (its Device object's), from 0 to 4194302. Without an address, a Who-Is finds the device by it. With one, the device at the address must be this instance, and is refused when it has another. See Finding the Device.
network_numberN-From 1 to 65534. The number of the device's network behind the BACnet router at address, with mac_address. With a device_instance and no address, it limits the Who-Is to that network.
mac_addressN-The device's address on network_number, behind the router at address; both are required with it. An MS/TP station's number, 0 to 254, as its tools show it (12); the octets of any address in hexadecimal, after 0x or with a colon or a hyphen between them (0x0C, 00:1A:2B:3C:4D:5E), seven at most; or the address and port of a BACnet/IP device (192.0.2.20:47808). Digits alone are a station's number, never hexadecimal.

* One of address and device_instance is required.

The Director's Socket​

FieldRequiredDefaultDescription
bind_addressN0.0.0.0The IPv4 address of Director the socket is bound to; every address by default, which is what to leave it at. See A Bind Address on Linux.
portN47808The UDP port the socket is bound to, BACnet/IP's, and the one the device is reached on unless device_port names another. From 1 to 65535. Every BACnet device of a Director that names the same bind_address and port shares the socket.
broadcast_addressN255.255.255.255Where a Who-Is is broadcast: the IPv4 broadcast address of the subnet (192.0.2.255), with a port after a colon when the network's is not port. The default goes out of the interface the system chooses: on a Director with more than one, name the subnet's own. Not used, and the device's log says so, with a BBMD.
bbmd_addressN-Host name or IPv4 address of the BBMD Director registers with as a foreign device, to reach the devices of another subnet. See Through a BBMD.
bbmd_portN47808The BBMD's UDP port, from 1 to 65535
bbmd_ttlN300Seconds a registration lasts, from 30 to 65535. It is made again when half of it has passed.

Requests​

FieldRequiredDefaultDescription
timeoutN3Seconds an answer is waited for, from 1 to 60: the APDU timeout. A device of a slow MS/TP line behind a router takes several.
max_retriesN3How often a request that is not answered is sent again, from 0 to 10: the number of APDU retries. A device that does not answer is given up after timeout × (max_retries + 1) seconds.
segmentationNtrueWhether Director takes an answer in segments. Off, it asks for answers that fit one message, and reads an answer that does not in smaller requests.
max_segmentsN64How many segments of an answer Director takes: 2, 4, 8, 16, 32, or 64. Not used, and the device's log says so, with segmentation off.
max_apdu_lengthN1476The longest message Director takes: 50, 128, 206, 480, 1024, or 1476, BACnet/IP's by default.

A device that fails these checks does not start, and its state is Configuration error; the message names the field. Examples are no device to read, an address or a port that is not one, an instance or a network out of range, a MAC without its network and its router, a network with an address and no MAC, or a value that is not a number or not one of its choices. A number is read whole: NaN, an infinity, or a fraction is refused, never converted.

Connection​

One Socket per Director​

BACnet/IP has no connection. A request leaves Director's UDP port and the device answers to that port, and what a device sends unasked, its I-Am to a Who-Is and the changes of value it reports, comes to the port too. The standard's port is 47808 on both sides, and a device that answers a broadcast Who-Is answers with a broadcast to it.

So Director binds one socket for each bind_address and port, opened by the first BACnet device that asks for it and closed when the last lets go of it, and hands each answer, I-Am, and change of value to the device it is for. An answer is taken only from the address its request went to, and a change of value only from the address its subscription was made at.

One request at a time goes to a BACnet device, whichever of the Director's devices asks: a controller answers a few at once at most, and one on an MS/TP line one.

Ports. The BACnet devices of a Director that bind one port share it, any number of them on 47808. The console's port check lets BACnet devices share a port, and refuses a device of another type that binds the port beside them, such as a UDP listener on 47808, and a BACnet device on a port a listener holds. Two BACnet devices that bind one port, one on every address (0.0.0.0) and one on a single address, are two sockets the system does not keep apart: the console refuses the second, and a Director given both from a configuration file starts the first and puts the second in Configuration error, naming both addresses. Two different addresses of the Director, one each, are two sockets.

A port another program on the Director's host holds, such as a BACnet workstation or a second BMS service, cannot be bound: the device is in Initialization error until it can. A Director that must run beside one binds another port, and reaches its devices by their addresses or through a BBMD.

A Bind Address on Linux​

A Director on two BACnet networks keeps bind_address at 0.0.0.0, one socket for both, and names each network's own broadcast_address on the devices of that network: where a broadcast goes is the device's setting, not the socket's.

Binding one address each is not the way on Linux, where a UDP socket bound to one address receives only the datagrams sent to that address. A broadcast, which is what most devices answer a Who-Is with, does not reach it, so a device found by its instance is not found, and a network's inventory finds only the device read. Finding devices by instance and a network inventory then need a BBMD, or the default 0.0.0.0. Devices read by their address, and those reached through a BBMD, which sends Director their I-Ams itself, are read either way. Windows hands a socket bound to one address the broadcasts of that address's subnet.

Container Directors​

On a container Director whose platform publishes no UDP port, which is the case on Azure Container Apps and Azure Container Instances, a device read by its address works: its answers come back the way its requests went. Finding a device by its instance and receiving changes of value need inbound UDP, which those platforms do not publish. See Device Listener Ports.

Finding the Device​

  • By its address. The device at address and device_port is asked which device it is (its Device object under the wildcard instance 4194303), then for the longest message it takes, whether it sends answers in segments, and its vendor. An older device that does not answer for that instance is asked with a Who-Is sent to it alone; one that answers neither cannot be read until its device_instance is named. With a device_instance named, a device that has another is in Configuration error, which says that the device at the address is not the one named. A host name is looked up every time the device is looked for.
  • By its instance. A Who-Is for that instance alone is broadcast to broadcast_address, for every network (routers pass it on), or for network_number alone when it is named. The device's I-Am says where it is, behind a router with its network and its MAC. The Who-Is is sent again every timeout, max_retries times, and then the device is not found: Connection error, with what to check.
  • Behind a BACnet router. A device of an MS/TP line, or of any network behind a BACnet router, is read through the router: every request names the device's network and its MAC. Named by address, network_number, and mac_address, it is asked at once; found by its instance, its I-Am names all three. A router that cannot pass a request on says so, and the device's log says that it has no route to that network, with the advice to check network_number.

A device that stopped answering is looked for again at the next reading, so a device found by its instance follows a change of address: it is read at its new address, and subscribed to anew.

Through a BBMD​

A broadcast does not leave Director's subnet. To find and hear the devices of another subnet, Director registers with that network's BBMD as a foreign device (Register-Foreign-Device, Annex J.5). Its Who-Is then goes to the BBMD, which sends it on, and the BBMD passes the I-Ams back; Director takes them from that BBMD alone. Requests and answers go straight between Director and the device, as IP routes them.

The registration lasts bbmd_ttl seconds and is made again when half of it has passed. One the BBMD refuses or does not answer is tried again after a second, then after twice the last wait, up to a minute; a BBMD that has forgotten Director is registered with again at once. The devices of a Director that name one BBMD share one registration, made for the shortest of their times to live. A device whose BBMD does not register Director is in Connection error, with the BBMD's answer. The BBMD's entry is not deleted when the device stops: it runs out.

Datasets​

The device's definitions are its datasets. In the console they are the datasets and profiles assigned to the device; see Datasets and Profiles. In a configuration file each dataset is a definition named by its type:

DatasetDefinitionDefinition IDKindOne record per
objectbacnet_object_collector33150metricreading of the dataset's table
covbacnet_cov_collector33151metricchange of value the device reports, or reading of an object it refuses to report
inventorybacnet_inventory_collector33100inventoryobject of every device a sweep reads
  • Any number of object and change of value datasets. Each is a table of its own, so a device reads as many as are assigned.
  • One inventory per device. A second inventory assigned to the same device is left out, and the device's log says so.
  • Pipelines. A dataset's records enter the pre-processing pipeline the dataset selects, or the device's when it selects none.
  • Nothing assigned is allowed. The device then only checks, every minute, that its BACnet device answers, so its connection state stays current, and writes nothing.
  • A dataset that cannot be read is left out, with a warning in the device's log that names it and says why: a table that is not a name, an object or a property that is not one, or an object or a name declared twice. The rest of the device runs. With every dataset assigned to it left out, the device stops with Configuration error.

Objects​

A bacnet_object_collector dataset is a table of the device's objects, whose properties are read every interval seconds, all in one reading, which is one record.

FieldRequiredDefaultDescription
tableY-The name the dataset's objects sit under in a record (see Records): letters, digits, and underscores, at most 64, not starting with a digit. confirmed, name, object, source, table, and time_remaining are reserved.
intervalN60Seconds between readings, 1 or more
labelsN-An object of names and values written on every record as ECS labels. At most 32, a value at most 256 characters.
pipeline_nameNthe device'sThe pre-processing pipeline the dataset's records enter

A dataset's inputs are its objects, at most 1024:

FieldRequiredDescription
nameNThe name the object's values are written under: letters, digits, and underscores, at most 64, not starting with a digit, once in the table. Left out, it is the object's type and instance (analog_input_3; type_300_3 for a vendor's own type).
objectYThe object's type and instance, with a comma, a colon, or a space between them. The type by its name in any way it is written (analog-input,3, Analog_Input:3, analogInput 3), by a short name (AI, AO, AV, BI, BO, BV, MSI, MSO, MSV, also MI, MO, MV, and DEV; AI3 too), or by its number, which names a vendor's own (300,3). The instance is 0 to 4194302. An object is declared once in a table.
propertiesNThe properties read of it: a list of their names in any way they are written (present-value, Present_Value, presentValue) or of their numbers, or text with a comma between them. present-value when left out or empty; at most 16, each once, never all.

Changes of Value​

A bacnet_cov_collector dataset is the same table, subscribed to with SubscribeCOV: the device sends an object's value when the subscription is made and whenever it changes, by its own rule (a binary or a multi-state object at any change, an analog one when it moves by its COV increment). Each notification is a record. Nothing is asked for between two changes.

FieldRequiredDefaultDescription
table, labels, pipeline_name--As an object dataset's
confirmedNfalseWhether the device waits for the acknowledgement of every notification, and sends it again when none comes. Unconfirmed notifications are not sent again when one is lost.
lifetimeN300Seconds a subscription lasts, from 60 to 86400. It is renewed when half of it has passed; a device that renews a subscription sends the object's value again, so a quiet object is written once every half lifetime.
intervalN60Seconds between the readings of the objects the device refuses to report the changes of

The inputs are an object dataset's. A notification carries the properties the standard has an object report, its present value and its status flags for most. Of those, the ones properties names are written, so an object that also names status-flags writes both, and one that names units alone writes nothing when notified.

An object whose device refuses a subscription is read on interval instead, as an object dataset's are, and written as a notification would have been, its record saying poll:

  • A device that has no SubscribeCOV rejects the first subscription: every object of it is read from then on, and it is not asked again while the device runs.
  • An object that reports no changes, or a device with no room for another subscription (a controller has room for a few dozen), answers with an error: that object is read, and asked for a subscription again once a lifetime has passed.
  • A subscription the device does not answer is asked for again 30 seconds later, and the device is in Connection error meanwhile.

Each is said once in the device's log, with the device's answer. A device that waits for changes or for its inventory's next sweep, and has heard nothing for a minute, asks its BACnet device for its system status, and is in Connection error when that is not answered.

Inventory​

A bacnet_inventory_collector dataset is what the network has, so that you can see what there is to read: a Who-Is sweep, and for each device that answers, its object list with every object's name, description, and units. One record per object.

FieldRequiredDefaultDescription
intervalN3600Seconds between sweeps, 60 or more, from the start of one to the start of the next
scopeNnetworknetwork sweeps every device that answers the Who-Is; device reads only the device this BACnet device reads, and asks the network nothing.
discovery_timeoutN5Seconds a sweep listens for I-Ams, from 1 to 120. The Who-Is is sent at its start and at its half.
device_instance_min, device_instance_maxN-The instances a sweep asks for, 0 to 4194302, the first no more than the second. Every device when both are left out.
max_devicesN256How many devices a sweep reads, from 1 to 4096: those of the lowest instances, and the device read itself
max_objectsN2000How many objects of one device a sweep reads, from 1 to 20000. A sweep reads 100000 objects in all at most.
labels, pipeline_nameN-As an object dataset's
warning

A network sweep reads every device's object list. A network inventory assigned to several devices runs once for each of them, so every one of them sweeps the network. Assign a scope: network inventory to one device of a network, and use scope: device for an inventory assigned to many.

The device read is in the sweep whether or not its I-Am came, and its objects are marked as the ones this device reads (bacnet.inventory.target). A sweep reads one device after another, one request at a time, in a routine of its own, so the device's values are still read, and its changes written, while the network is swept. Of each device it reads what its Device object says of it (its name, vendor, model, firmware and application software versions, location, and description), its object list, and every object's name, description, and units where the object has them.

From its second sweep on, an inventory marks every record with how the object compares with the last complete sweep (event.action): added, changed (its name, its description, or its units; for a Device object also what the device says of itself), unchanged, or removed, an object that is gone being written once more as the last sweep held it. A device that answers the Who-Is and then no more, or says it is busy, is written as one record with error.type incomplete, and its objects are kept, not removed. The comparison is kept in the Director's memory: a restart, a change to the device or its datasets, or a move to another Director marks nothing at its first sweep.

Requests​

  • Several properties a request. The properties of a reading are asked for with ReadPropertyMultiple, as many in a request as fit the longest message the device takes, 64 at most. An answer that does not fit is asked for again in halves, and the device is asked for no more than that from then on.
  • A property at a time. A device that has no ReadPropertyMultiple rejects it, and is read with ReadProperty, one property a request, from then on. A device that refuses a whole request for one property it does not have has the request split until the property is found, and that property is asked for alone for ten minutes.
  • A property the device refuses (an unknown object or property, or denied read access) is left out of the record, and said once in the device's log.
  • A device that cannot answer now (busy, being configured, out of resources) ends the reading: the device is in Connection error, is looked for again at the next reading, and is asked then for the same.
  • Segments. An answer longer than a message comes in segments, when the device sends them and segmentation is on. An answer of more segments than max_segments, or one whose segments stop coming for a timeout, is given up. Director never sends a request in segments: its requests are cut to the device's longest message.
  • An object list is asked for whole. When it does not fit and cannot come in segments, its length is read, and then its elements.

Readings​

  • At start the device reads every object dataset once, makes its subscriptions one object after another, and starts its inventory's first sweep.
  • Each object dataset on its own interval after that, from the start of one reading to the start of the next.
  • Datasets due at the same time are read one after the other. A reading that takes longer than its interval delays the next, which starts a moment after it ends.
  • Subscriptions are renewed when half their lifetime has passed.

Values​

What the device answersWritten as
A Real, a DoubleA number; a Real as the shortest decimal that reads back as it (72.3). One that is not a number, or is an infinity, has no value.
An Unsigned, a SignedA number
A Booleantrue or false
The present value of a binary input, output, or value (and its alarm-value, feedback-value, relinquish-default)true for active, false for inactive
Status flagsin_alarm, fault, overridden, and out_of_service, a switch each
Another bit stringIts bits as text (1010)
units (and a loop's output-units and the like)The unit's name as the standard writes it (degrees-celsius, kilowatt-hours, no-units); its number as text for a vendor's own unit, or one newer than the table's 253
event-state, reliability, a device's system-status and segmentation-supported, a polarity, an object-typeThe standard's name (normal, no-fault-detected, operational); the number as text for a vendor's own
Any other enumerationIts number
A character stringText, read as UTF-8, UCS-2, UCS-4, or ISO 8859-1 as its character set says. The double-byte sets (IBM's and Microsoft's DBCS, JIS X 0208) are not read.
An octet stringIts octets in lowercase hexadecimal
A date, a timeText: 2026-10-14, 13:05:09.25; a part that is not specified is *
An object identifieranalog-input,3
Several values (state-text, priority-array, object-list)A list of them, a Null among them null
A Null aloneNo value
A structure (a reference, a schedule, a date and time, a list of them)Not read

A property with no value, or whose value is not read, is left out of the record, and said once in the device's log for as long as it lasts.

Records​

Every record is one ECS 8.11 document with the following fields:

FieldValue
@timestampThe reading's time; for a change of value, when its notification arrived; for an inventory's record, when its device was read
ecs.version8.11.0
event.modulebacnet
observer.nameThe device's name in the console
observer.hostnameThe Director's host name
observer.vendor, observer.productVirtualMetric, BACnet
network.protocol, network.transportbacnet, udp
labels.<name>The dataset's labels, when it has any

The records of an object and of a change of value dataset also name the BACnet device read:

FieldValue
server.address, server.portThe address the device was configured with, or the one its I-Am came from when it was found by its instance (its router's, behind one), and the port
server.ipThe address, as an IP address
bacnet.device.instanceThe device's instance
bacnet.device.addressWhere it is reached (192.0.2.10:47808)
bacnet.device.network, bacnet.device.macBehind a router, its network's number and its MAC, in hexadecimal with a colon between octets

Object Records​

event.dataset is bacnet.object, event.kind metric, event.category host, and event.type info.

FieldValue
bacnet.object.tableThe dataset's table
bacnet.object.<table>.<name>.<property>The value of each property that was read of each object, the property by its name with underscores (present_value, status_flags, units; property_600 for a vendor's own)
{
"@timestamp": "2026-10-14T12:00:00Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.object", "kind": "metric", "category": ["host"], "type": ["info"] },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"server": { "address": "192.0.2.10", "port": 47808, "ip": "192.0.2.10" },
"network": { "protocol": "bacnet", "transport": "udp" },
"labels": { "building": "north" },
"bacnet": {
"device": { "instance": 1234, "address": "192.0.2.10:47808" },
"object": {
"table": "ahu",
"ahu": {
"supply_temp": {
"present_value": 18.5,
"status_flags": { "in_alarm": false, "fault": false, "overridden": false, "out_of_service": false },
"units": "degrees-celsius"
},
"fan_status": { "present_value": true },
"fan_speed": { "present_value": 3 }
}
}
}
}

The values sit under the table's name and the object's, so a property keeps one type, and two tables can name an object alike. An object none of whose properties was read is absent from that record; a record with no object is not written.

Change of Value Records​

event.dataset is bacnet.cov, event.kind metric, event.category host, and event.type info. One record per notification, and one per reading of an object that is read instead.

FieldValue
bacnet.cov.tableThe dataset's table
bacnet.cov.nameThe object's name in the table
bacnet.cov.objectThe object (binary-value,7)
bacnet.cov.sourcenotification, or poll for an object that is read
bacnet.cov.confirmedA notification's: whether the device waited for its acknowledgement
bacnet.cov.time_remainingA notification's: the seconds the device still keeps the subscription
bacnet.cov.<table>.<name>.<property>The values, as an object record's
{
"@timestamp": "2026-10-14T12:03:41.512Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.cov", "kind": "metric", "category": ["host"], "type": ["info"] },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"server": { "address": "192.0.2.10", "port": 47808, "ip": "192.0.2.10" },
"network": { "protocol": "bacnet", "transport": "udp" },
"bacnet": {
"device": { "instance": 1234, "address": "192.0.2.10:47808" },
"cov": {
"table": "alarms",
"name": "filter_alarm",
"object": "binary-value,7",
"source": "notification",
"confirmed": true,
"time_remaining": 284,
"alarms": {
"filter_alarm": {
"present_value": true,
"status_flags": { "in_alarm": true, "fault": false, "overridden": false, "out_of_service": false }
}
}
}
}
}

Inventory Records​

event.dataset is bacnet.inventory, event.kind state, and event.category host; event.type is info, or creation, change, or deletion with event.action added, changed, or removed. An inventory record has no server: the object may be any device's.

FieldValue
event.actionFrom the second sweep on: added, changed, unchanged, or removed
bacnet.inventory.targetWhether the object's device is the one this BACnet device reads
bacnet.inventory.device.instanceThe device's instance
bacnet.inventory.device.addressWhere it is reached; with network and mac behind a router
bacnet.inventory.device.nameIts Device object's name
bacnet.inventory.device.vendor_name, vendor_id, model_name, firmware_revision, application_software_version, location, descriptionWhat it says of itself; what it does not say is left out
bacnet.inventory.device.max_apdu_length, segmentationThe longest message it takes, and what it says of its segmentation (segmented-both, no-segmentation)
bacnet.inventory.device.objectsHow many objects it says it has
bacnet.inventory.object.identifierThe object, as an object dataset's object is written (analog-input,1)
bacnet.inventory.object.type, instanceIts type's name and its instance
bacnet.inventory.object.name, description, unitsIts name, description, and units, those it has
error.type, error.messageincomplete and why, on the one record of a device whose objects could not be read
{
"@timestamp": "2026-10-14T12:00:07Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.inventory", "kind": "state", "category": ["host"], "type": ["change"], "action": "changed" },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"network": { "protocol": "bacnet", "transport": "udp" },
"bacnet": {
"inventory": {
"target": true,
"device": {
"instance": 1234, "address": "192.0.2.10:47808", "name": "AHU-1", "vendor_name": "Example Controls",
"vendor_id": 260, "model_name": "AC-900", "firmware_revision": "4.2.1", "max_apdu_length": 1476,
"segmentation": "segmented-both", "objects": 153
},
"object": {
"identifier": "analog-input,1", "type": "analog-input", "instance": 1,
"name": "AHU-1 SAT", "description": "Supply air temperature", "units": "degrees-celsius"
}
}
}
}

Device State​

The connection state follows the BACnet device:

  • Connected while it answers any property of any of the device's datasets with its value, takes a subscription, or reports a change.
  • Configuration error while it answers but refuses everything the datasets ask of it (objects it does not have), with the refusal and what it means; while the device at the address is not the instance named; and while two devices of the Director ask for one port on two addresses. A configuration that cannot run is a Configuration error too (see Configuration).
  • Connection error while it is not found, does not answer, says it cannot answer now, its router has no route to it, or the Director's BBMD does not register the Director.
  • Initialization error while Director cannot bind the port.

The state follows every dataset's latest reading, not only the datasets of the reading in progress. Each failure is said once in the device's log, again every 30 minutes for as long as it lasts, and its end once. A record that could not be written, or a failure in one reading, one notification, or one device of a sweep, affects that one alone and does not change the device's state.

Any change to the device or to its datasets restarts it: its subscriptions are cancelled and made again, and the comparison of its inventory starts anew. When the device stops, it cancels its subscriptions for three seconds at most; what it could not cancel, the BACnet device ends when the lifetime runs out, which is why a lifetime is never unlimited.

note

The BACnet device is not simulated. A dataset's records are the values of your own objects, and an inventory's the objects of a building, shapes a generated sample cannot stand in for.

What It Sends​

Everything the device sends, so that "it only reads" can be checked against a capture:

MessageWhen
Who-Is (unconfirmed)To find a device by its instance, to ask a device at an address which it is when it does not answer for instance 4194303, and at the start and the half of an inventory's sweep
ReadProperty, ReadPropertyMultipleEvery reading
SubscribeCOVTo make, renew, and cancel a subscription
SimpleACK, Error (unknown-subscription)To a confirmed notification of a change of value
Reject (unrecognized-service), AbortTo any other confirmed request it is sent
SegmentACK, AbortWhile an answer comes in segments
Register-Foreign-Device (BVLL)To its BBMD, when it names one

It sends no WriteProperty, WritePropertyMultiple, AddListElement, RemoveListElement, CreateObject, DeleteObject, AtomicWriteFile, DeviceCommunicationControl, ReinitializeDevice, TimeSynchronization, AcknowledgeAlarm, LifeSafetyOperation, or private transfer, no I-Am, and nothing that writes or deletes an entry of a BBMD's tables. Director has no Device object of its own, and answers no Who-Is.

Not Supported​

  • BACnet/SC (TLS over WebSockets), BACnet/IPv6, and the data links a Director has no port for: MS/TP over a serial line, BACnet Ethernet, ARCNET, and LonTalk. Their devices are read through a BACnet router, as any routed device is.
  • Writing of any kind (see What It Sends).
  • Alarms and events: event notifications, GetEventInformation, GetAlarmSummary, and notification classes. An object's event-state and status-flags are read as properties.
  • Trend logs (ReadRange), files (AtomicReadFile), schedules, and calendars, whose values are structures.
  • SubscribeCOVProperty and the COV-multiple services: a subscription is to an object, and carries what the standard has it report.
  • An element of an array as a dataset's property: a priority-array is read whole.
  • A Device object of Director's own: Director answers no Who-Is and no ReadProperty, so it is in no workstation's device list. Unconfirmed notifications need none.
  • Requests in segments: a request is cut to the device's longest message instead.
  • Character strings in a double-byte set, and engineering units newer than the unit table (252, 254, and the 47808 range), which are written as their numbers.
  • Network security (clause 24), which the standard has since removed.

Compatibility​

The device follows ASHRAE 135 (BACnet), clauses 5.4, 6, 20 and 21, and Annex J (BACnet/IP). If one of your devices, routers or BBMDs answers differently from the standard, contact VirtualMetric support with its make and model.

Console​

In the console, go to Fleet Management > Devices > BACnet, listed under Protocols, and click Add new device. The wizard's steps are:

  1. General settings, as on every device.

  2. Connection settings: Addressing method chooses how the controller is named, and the step shows only that method's fields. The method is the console's own choice: Director never sees it, only the properties it fills.

    • Address shows Address, Controller port (Optional), and Device instance (Optional).
    • Device instance shows Device instance and Network number (Optional), which keeps the Who-Is to one network.
    • BACnet router shows Router address, Router port (Optional), Network number, MAC address, and Device instance (Optional).

    Port (47808) is on this step whichever method is chosen.

  3. Network settings: Foreign device registration, off by default. Off, the step shows Broadcast address (Optional) (255.255.255.255); on, BBMD address, BBMD port, and Registration lifetime (300).

  4. Advanced settings: Listen address (Optional) (0.0.0.0), APDU timeout (3), Maximum APDU retries (3), Segmentation, Maximum segments (64, shown only while Segmentation is on), and Maximum APDU length (1476).

  5. Data collection: the BACnet datasets the device reads.

  6. Review and create.

The device's page has the same tabs, and Data collection. A device saved through the API names no addressing method, so its page opens on the method its properties imply: a mac_address, or a network_number beside an address, is BACnet router; an address alone is Address; a device_instance with no address is Device instance.

The BACnet datasets are created in Fleet Management > Datasets; see Management. There are three types:

  • BACnet Objects: Table name, Interval (Optional), the Objects table, the labels, and the pre-processing pipeline. Each object row is the object (as analog-input,3), its Field (Optional), the row's name in Objects, and its Properties (Optional): the common ones chosen from a list, and Other properties (Optional) for any other, by its name or its number. An empty field is named by Director, and the table shows the name it gets, as analog_input_3 (automatic). A row with no property reads present-value.
  • BACnet Changes of Value: the same settings and table, plus Confirmed notifications and Subscription lifetime (Optional).
  • BACnet Inventory: Sweep scope (Every controller on the network or Only the controller the device reads), Interval (Optional), and, all optional, Discovery timeout, Maximum controllers, Minimum device instance, Maximum device instance, and Maximum objects. With Only the controller the device reads, the fields that only a sweep of the network uses are not shown. A device reads one inventory: the data selection drawer refuses a second.

Import objects, beside Add object, adds many rows to an object or a change of value table at once. It takes lines of text, an object a line, as object,field,property;property (analog-input,3,supply_temp,present-value;status-flags), or the inventory dataset's records as the device page's Live data exports them, as CSV or JSON, pasted or uploaded as a file of up to 10 MB. Of an inventory's records, only the objects of the controller the device reads are added. An object already in the table is passed over, and each line that was refused is listed by its number. This is how the objects an inventory found are put into a table.

The console refuses two things that Director takes from a configuration file: a duration (read as whole seconds, as 5s or 1m) or an environment variable (${VAR}) in one of the device's number fields, and ${VAR} in a table name or an object. The dataset editors take an environment variable in any number field, and a duration in one that counts seconds.

Examples​

By Address, with Objects and Changes of Value​

Reading an air handling unit on the Director's own subnet by its address: its temperatures, fan, and valve every 30 seconds, and its filter and frost alarms as they change, with confirmed notifications...

- id: 685300001
name: ahu-1
description: Air handling unit 1, on the director's own subnet
type: bacnet
status: true
properties:
address: "192.0.2.10"
bind_address: "0.0.0.0"
port: 47808
timeout: 3
max_retries: 3
definitions:
- name: bacnet_object_collector
status: true
properties:
table: ahu
interval: 30
labels:
building: north
floor: "3"
inputs:
- name: supply_temp
properties:
object: analog-input,1
properties: [present-value, status-flags, units]
- name: return_temp
properties: { object: "analog-input,2" }
- name: fan_status
properties: { object: "binary-input,1", properties: [present-value, status-flags] }
- name: fan_speed
properties: { object: "multi-state-value,1" }
- name: cooling_valve
properties: { object: "analog-output,1", properties: [present-value, out-of-service] }
- name: bacnet_cov_collector
status: true
properties:
table: alarms
confirmed: true
lifetime: 300
interval: 60
inputs:
- name: filter_alarm
properties: { object: "binary-value,7", properties: [present-value, status-flags] }
- name: frost_alarm
properties: { object: "binary-value,8" }

By Instance, Through a BBMD, with a Network Inventory​

Finding a chiller on another subnet by its instance, through that subnet's BBMD, reading its values every minute, and sweeping the instances 2000 to 2999 of the network every hour. This is the one device of the network the network inventory is assigned to...

- id: 685300002
name: chiller-2
description: Chiller 2, found by its instance through the BBMD of its subnet
type: bacnet
status: true
properties:
device_instance: 2100
bbmd_address: "198.51.100.1"
bbmd_port: 47808
bbmd_ttl: 300
timeout: 5
definitions:
- name: bacnet_object_collector
status: true
properties:
table: chiller
interval: 60
inputs:
- name: leaving_water_temp
properties: { object: "analog-input,1", properties: [present-value, status-flags] }
- name: entering_water_temp
properties: { object: "analog-input,2", properties: [present-value, status-flags] }
- name: load
properties: { object: "analog-value,10" }
- name: run_hours
properties: { object: "accumulator,1" }
- name: mode
properties: { object: "multi-state-value,2", properties: [present-value, event-state, reliability] }
- name: bacnet_inventory_collector
status: true
properties:
interval: 3600
scope: network
discovery_timeout: 5
device_instance_min: 2000
device_instance_max: 2999
max_devices: 256
max_objects: 2000

Behind a BACnet Router​

Reading a VAV box of an MS/TP line, station 12 on network 5, behind the BACnet router of floor 3. A slow line takes a longer timeout and answers that fit one message. Its inventory covers this device alone...

- id: 685300003
name: vav-3-12
description: A VAV box of an MS/TP line, behind the BACnet router of floor 3
type: bacnet
status: true
properties:
address: "192.0.2.1"
network_number: 5
mac_address: 12
timeout: 10
max_retries: 2
segmentation: false
definitions:
- name: bacnet_object_collector
status: true
properties:
table: vav
interval: 120
inputs:
- name: zone_temp
properties: { object: "analog-input,1" }
- name: zone_setpoint
properties: { object: "analog-value,1" }
- name: damper_position
properties: { object: "analog-output,1" }
- name: occupied
properties: { object: "binary-value,1" }
- name: bacnet_inventory_collector
status: true
properties:
scope: device
interval: 86400

A Socket of Its Own​

Reading the main energy meter at the BMS front end's address, as instance 3100, from a socket on one address of the Director and port 47809, for a host where another BACnet program holds 47808. The meter is still reached on 47808. On Linux a socket bound to one address hears no broadcast, which is why this device names its address...

- id: 685300004
name: main-meter
description: The main energy meter, read from a socket of its own
type: bacnet
status: true
properties:
address: bms.example.internal
device_instance: 3100
bind_address: "192.0.2.5"
port: 47809
device_port: 47808
broadcast_address: "192.0.2.255:47808"
definitions:
- name: bacnet_cov_collector
status: true
properties:
table: meter
lifetime: 3600
inputs:
- name: active_energy
properties: { object: "accumulator,1" }
- name: active_power
properties: { object: "analog-input,10", properties: [present-value, status-flags] }