Skip to content

BACnet/IP

BACnet (Building Automation and Control Network) is the open protocol of building automation, standardised as ANSI/ASHRAE 135 and ISO 16484-5. Air handling units, boilers, room controllers, lighting and meters use it to exchange typed values. BACnet/IP (Annex J of the standard) carries BACnet over IPv4 and UDP, by default on port 47808 (hexadecimal 0xBAC0).

This page explains the protocol as the OT Fabric projects use it. It is an orientation, not conformance language: for normative detail use a licensed copy of ANSI/ASHRAE 135.

A BACnet device does not expose a register map. It exposes objects, and each object has properties.

  • A device is one controller on the network. It has a device instance, a number from 0 to 4194302 that must be unique in the whole BACnet internetwork. The device is itself an object, device,<instance>, whose properties describe it: vendor, model, firmware, protocol revision, maximum APDU size, segmentation support, the services and object types it supports, and its object-list.
  • An object is one thing the device holds: an input, an output, a value, a schedule, a trend log, a file, a notification class. It is identified by a type and an instance number, for example analog-input,3. The pair is the object identifier.
  • A property is one fact about an object: present-value, object-name, units, status-flags, description. Some properties are arrays (indexed from 1, with index 0 holding the length) or lists.

Values on the wire are typed with application tags: Null, Boolean, Unsigned, Signed, Real, Double, OctetString, CharacterString, BitString, Enumerated, Date, Time and ObjectIdentifier, plus constructed values built from them. A device refuses a write with the wrong type, so a client must know or look up the type a property holds.

A commandable property, usually the present-value of an output or a value object, keeps a priority-array with 16 slots. The object takes the value of the highest non-empty slot (1 is the highest). Writing a value at a priority commands that slot. Writing NULL at the same priority relinquishes it. When every slot is empty, the object falls back to its relinquish-default. A value written at priority 8 (the conventional manual operator priority) overrides a control program writing at a lower priority until it is relinquished.

A BACnet/IP datagram is peeled from the outside in:

UDP datagram
└─ BVLC BACnet Virtual Link Control (BACnet/IP framing)
└─ NPDU Network Protocol Data Unit (routing, hop count, message type)
└─ APDU Application Protocol Data Unit (service PDU)
└─ service payload (Who-Is, ReadProperty, ...)
Layer Role
BVLC BACnet/IP framing: Original-Unicast-NPDU, Original-Broadcast-NPDU, Forwarded-NPDU, foreign-device registration, broadcast distribution table (BDT) and foreign device table (FDT) management, BVLC-Result
NPDU Local or routed delivery (destination network DNET and address DADR, source SNET/SADR), hop count, network-layer messages such as Who-Is-Router-To-Network
APDU Confirmed and unconfirmed requests, SimpleACK, ComplexACK, Error, Reject, Abort, SegmentACK
Service The payload: ReadProperty, WriteProperty, SubscribeCOV and the rest

Unconfirmed services such as Who-Is, I-Am and unconfirmed COV notifications are fire-and-forget: no invoke ID, no acknowledgement. Confirmed services such as ReadProperty, WriteProperty and SubscribeCOV carry an invoke ID (0 to 255). The requester waits for a matching answer:

Answer Meaning
SimpleACK Success without data (a write)
ComplexACK Success with data (a read)
Error The device understood and refused, with an error class and code such as object / unknown-object
Reject The request was malformed or the service is not recognised
Abort The transaction was ended, for example because a reply would not fit

There is no connection and no session. BACnet/IP is plain UDP: every device has one socket and sends datagrams to the others.

Every device announces the largest APDU it accepts, between 50 and 1476 octets on BACnet/IP. A message larger than that has to be segmented: split into numbered segments that the receiver acknowledges with SegmentACK, in windows. A device announces whether it can send segments, receive them, both, or neither. A device that cannot segment answers a request whose reply would be too large with an Abort (segmentation-not-supported or buffer-overflow), and the client has to ask for less, for example by reading a long object-list element by element.

A client finds devices with Who-Is, optionally limited to a range of device instances. Every matching device answers with I-Am: its device instance, maximum APDU size, segmentation support and vendor ID. Devices usually send the I-Am as a broadcast to port 47808, so a client must be bound to that port to hear the answers, and only one program on a host can hold it. Who-Has and I-Have do the same for an object, by name or identifier.

A BACnet address is not always an IP address and port:

  • A local station is reached directly over BACnet/IP at its IP and port.
  • A remote station sits on another BACnet network, behind a BACnet router. It is addressed by network number and MAC address (DNET + DADR), and the datagram goes to the router’s IP and port. A device on an MS/TP (RS-485 field bus) segment is reached this way; its MAC is its station number.
  • Local, remote and global broadcasts are used for discovery and network messages.

Clients learn routers with Who-Is-Router-To-Network and the I-Am-Router-To-Network replies.

IP routers do not forward broadcasts, so a Who-Is stops at the edge of the IP subnet. A BBMD (BACnet/IP Broadcast Management Device) carries broadcasts between subnets as Forwarded-NPDU messages, using its broadcast distribution table. A host on a subnet without a BBMD can register with a remote BBMD as a foreign device, for a time-to-live (TTL) that it must renew. It then sends its broadcasts to the BBMD as Distribute-Broadcast-To-Network and receives forwarded broadcasts. The registration appears in the BBMD’s foreign device table.

Instead of polling, a client can subscribe to change of value (COV) with SubscribeCOV (for an object) or SubscribeCOVProperty (for one property, with a COV increment). The device then sends a COV notification whenever the value changes by more than the increment. A subscription has a lifetime and must be renewed. Notifications can be confirmed or unconfirmed, and they are sent to the address the subscription came from.

Alarms and events are a separate path. Objects with intrinsic reporting, or Event Enrollment objects, change event state (normal, offnormal, high-limit, fault …) and send EventNotification to the recipients of their Notification Class. Clients query active events with GetEventInformation or GetAlarmSummary and acknowledge them with AcknowledgeAlarm, quoting the event’s time stamp.

Trend Log objects keep a buffer of records, read with ReadRange by position, sequence number or time.

BACnet/IP has no authentication and no encryption. Anyone who can reach a device on UDP 47808 can read it, write to it and restart it. Anyone on the path can read, forge or alter traffic, including Who-Is, I-Am, property responses and COV notifications. A device instance number is not an identity. The passwords of DeviceCommunicationControl and ReinitializeDevice travel in clear text.

BACnet Secure Connect (BACnet/SC) is the secured variant of the standard; the OT Fabric projects do not implement it. Keep building automation traffic on isolated, trusted networks and never expose UDP 47808 to the Internet.

BACnet is the open protocol of building automation: HVAC plant, room and zone controllers, lighting, access and life safety systems, and meters. A building management system (BMS) is typically a supervisory BACnet client that discovers the controllers, reads their points, trends them and commands setpoints.

For energy work this is where building loads become visible and controllable. Energy meters, chillers, boilers and air handling units publish their consumption and state as BACnet objects, and energy management or demand response software reads trend logs and commands setpoints and schedules through the same services. Field buses such as MS/TP often sit behind BACnet/IP routers, which is why routed addressing matters even for an IP-only client.

Term Meaning
APDU Application Protocol Data Unit: the service message
NPDU Network Protocol Data Unit: routing information around the APDU
BVLC BACnet Virtual Link Control: BACnet/IP framing
BBMD BACnet/IP Broadcast Management Device
BDT, FDT Broadcast distribution table, foreign device table of a BBMD
COV Change of value
DNET, DADR Destination network number and address in the NPDU
Invoke ID Number matching a confirmed request to its answer
MS/TP Master-Slave/Token-Passing, the RS-485 BACnet field bus
Priority array 16 command slots of a commandable property
Client only Not yet validated on physical controllers
Project What it is
go-bacnet Pure-Go BACnet/IP client library, no dependencies outside the standard library
bacnetctl Command-line client for discovery, reads, writes, COV monitoring, alarms, trends, files and network diagnostics
bacnet-interop Dockerized reference stacks (bacnet-stack, BACpypes3, BACnet4J, Worldiety, System.IO.BACnet) with fixtures and routed and BBMD scenarios

Source code: github.com/otfabric/go-bacnet, github.com/otfabric/bacnetctl, github.com/otfabric/bacnet-interop. Protocol landing page: /protocols/bacnet/. New to OT Fabric? Start with Getting started.