BACnet Basics BACnet

BACnet is the protocol most modern building automation runs on. If you touch a chiller plant control panel, a VAV (variable-air-volume) box controller, a JACE or EBO supervisor (the gateways and servers that supervise a building's controllers), a Distech actuator, or a Honeywell rooftop unit, you're almost certainly talking to BACnet. The history, for background: ASHRAE started work on it in 1987 to give building controls one common language that didn't belong to any single vendor, and ASHRAE 135 — the standard that defines it — has been a moving target ever since, picking up new object types and services as the industry's needs have changed. This page is the explainer the BACnet/IP Hex Converter tool's footnote keeps pointing toward.

What BACnet is, and isn't

The defining BACnet idea is that devices describe themselves. A BACnet device on the network doesn't just expose a block of memory full of numbers; it exposes a list of named, typed objects, each with named, typed properties. Ask a device what it has, and it answers. Ask any individual object for its name, units, or current value, and it answers. The protocol carries enough metadata that a client can walk into a building it has never seen before, query a controller, and produce a working point list. That alone makes it feel different from anything older.

The contrast that comes up most is with Modbus. Modbus is deliberately dumb — a register is just sixteen bits, and the only thing that knows whether those bits are a temperature in tenths of a degree or a packed status word is the vendor's manual. BACnet flips this. A BACnet Analog Input object knows its Object_Name, its Units, its Description, its current Present_Value, its high and low limits, and a couple dozen other properties — none of which need to live in a separate document. The protocol carries the labels and the values together.

The other thing BACnet isn't is one specific wire. ASHRAE 135 defines the protocol logic — objects, properties, services, addressing — once, and then defines several data-link options for carrying that logic over different physical media. The two you'll meet in the field are BACnet/IP (the protocol over UDP datagrams on a normal Ethernet/IP network) and BACnet MS/TP (the protocol over RS-485 twisted-pair, with a token-passing master ring). Newer options like BACnet/SC (Secure Connect, over TLS WebSockets) and the older BACnet/Ethernet exist but are rarer in BAS work. The object model and the services don't care which one is underneath.

Devices, objects, properties

Three nested layers carry everything. A device is the controller — a JACE, a VAV box controller, a chiller's onboard BACnet card, an MS/TP thermostat. Each device has one globally unique device instance number on the BACnet network (a value from 0 to 4,194,302; you'll often see it written as device:1001) and exposes itself as one Device object.

Inside the device live the objects. Every input, output, setpoint, schedule, alarm, calendar entry, file, and logic point the device wants to make visible on the network is an object. Each object has a type (Analog Input, Binary Output, Multi-state Value, Schedule, …) and an instance number that is unique within the device. Together those two values form the object's Object_Identifier — packed into a single 32-bit value as 10 bits of object type plus 22 bits of instance number, but always written as the human-readable pair, e.g. analog-input,3 or AI:3.

Inside the object live the properties. Every object has at least Object_Identifier, Object_Name, Object_Type, and a current Present_Value; most have a dozen or more on top of that. An Analog Input typically also exposes Units, Description, Status_Flags, High_Limit, Low_Limit, COV_Increment, and several others. The properties are where the actual data lives — every read and every write targets a specific property on a specific object on a specific device.

Device, object, property hierarchy A device box labeled "device:1001" contains four object boxes — analog-input,1; analog-output,1; binary-value,3; and device,1001 itself. An arrow leads from the analog-input,1 object to an exploded property list to the right showing Object_Identifier, Object_Name set to "OAT", Object_Type set to ANALOG_INPUT, Present_Value set to 58.4, Units set to DEGREES_FAHRENHEIT, and Status_Flags. The point is that the device contains objects, and each object contains named, typed properties. device:1001 (a controller) AO:1 AI:1 BV:3 SCHED:1 DEV:1001 objects inside the device analog-input,1 · properties Object_IdentifierAI:1 Object_Name "OAT" Object_Type ANALOG_INPUT Present_Value 58.4 Units DEGREES_FAHRENHEIT Description "Outside air temp" Status_Flags {F, F, F, F} COV_Increment 0.5

The object types group into a handful of recognizable families. The analog/binary/multi-state split is the one to hold in your head:

Analog · AI · AO · AV

32-bit floating-point values. Analog Input for a sensor reading, Analog Output for a hardware output the controller drives, Analog Value for a setpoint or computed value that lives only in software.

Binary · BI · BO · BV

Two-state values (ACTIVE / INACTIVE). Binary Input for a contact closure, Binary Output for a relay the controller drives, Binary Value for a software-only enable or override flag.

Multi-state · MSI · MSO · MSV

Enumerated values — three or more named states, like OFF / LOW / HIGH or AUTO / HEAT / COOL / OFF. The state names live in the object's State_Text property, addressed by 1-based integer.

Device · DEV

The controller itself, exposed as an object. Carries the device instance number, the vendor ID, the model, the firmware version, and the list of every other object the controller hosts.

Beyond these are Schedule, Calendar, Trend Log, Notification Class, File, Loop, Group, and several others — each its own object type with its own property set. Most BAS work touches the analog / binary / multi-state families, the Device object for discovery, and the Schedule family for time-of-day logic; the rest show up when a specific feature does.

The services you'll see

A service is a verb against the object model — the request a client puts on the wire to read a property, write a property, acknowledge an alarm, or subscribe to a change notification. ASHRAE 135 defines roughly thirty-five services; a handful do almost all the work in a normal BAS:

  • ReadProperty — fetch one property of one object. The everyday read: "what's Present_Value on AI:3 of device:1001?" The response carries the typed value (real, integer, boolean, enumerated, string) the property holds.
  • WriteProperty — set one property of one object. The everyday write: "set Present_Value on AO:1 to 75 % at priority 16." On commandable objects, the priority is part of the write — see the next section.
  • ReadPropertyMultiple / WritePropertyMultiple — fetch or set many properties in a single request. Polling a controller for a graphic's worth of points uses these, not a thousand individual ReadProperty calls; the network overhead is the same for one property or thirty.
  • SubscribeCOV + ConfirmedCOV-NotificationChange of Value. Instead of polling, the client subscribes once and the device sends a notification each time the value changes by more than the property's COV_Increment. Push instead of pull. Where Modbus forces a client to poll often enough to catch a change, BACnet lets the device tell the client when one happens. Subscriptions have a lifetime; clients re-subscribe before it expires.
  • Who-Is / I-Am — the pair of unconfirmed broadcasts that handle discovery. Their own section is next.

Other services — AcknowledgeAlarm, ReinitializeDevice, AtomicReadFile, CreateObject, the time-synchronization services — show up when a specific feature does. The five above carry almost every everyday read, write, and notification across a building. The fuller reference — the confirmed / unconfirmed split, the service families, and how BIBBs say which of these a given device actually supports — is BACnet Services.

The priority array — BACnet's command stack

Every commandable object — Analog Output, Binary Output, and Analog/Binary/Multi-state Value when configured commandable — has a special property called Priority_Array: a 16-slot array, indexed 1 to 16, that holds pending commands. A WriteProperty against Present_Value does not simply overwrite the value; it writes into one slot of the array, at a chosen priority. The object's actual Present_Value is then computed as the value in the lowest-numbered non-null slot, or — if every slot is null — the value of the Relinquish_Default property.

Slot 1 is the highest priority and slot 16 is the lowest. The convention across the industry is that low slot numbers are reserved for high-stakes commands (slot 1 is manual life-safety; slots 5 and 6 are critical-equipment / minimum-on-off), slot 8 is the operator's manual override, and slot 16 is the slot the BMS's normal sequence writes from. Slots 9–15 are largely available for application-specific use. The full reservation table is in ASHRAE 135; for everyday troubleshooting, "slot 8 beats slot 16, and a null at slot 8 lets slot 16 take over" is most of what you need.

Walking the worked example: the BMS's sequence writes a damper position of 65 % to slot 16. A tech, standing at the operator workstation, hand-overrides the same damper to 0 % at slot 8. The object's Present_Value now resolves to 0 % — the slot-8 override wins because it sits at a lower (higher- priority) index than the sequence at slot 16. When the tech is done, they write null into slot 8 (the same WriteProperty request, with a null value). Slot 8 becomes empty; the lowest non-null slot is now slot 16; the damper drops back to 65 %. The sequence never had to be told anything; it just becomes the new winner.

Priority array — slot 8 override beats slot 16 sequence A vertical stack of sixteen labeled slots numbered 1 at the top to 16 at the bottom. Slot 1 is labeled "life safety" and holds null. Slot 8 is labeled "manual override" and holds 0 percent, highlighted in the accent color. Slots 2 through 7 and 9 through 15 are null and dimmed. Slot 16 is labeled "BMS sequence" and holds 65 percent. An arrow from slot 8 points to a Present_Value box on the right showing 0 percent and labeled "lowest non-null slot wins." A note at the bottom says: write null to slot 8 and the value drops to 65 percent because slot 16 becomes the lowest non-null slot. Priority_Array of AO:1 Present_Value 1 life safety null 2 null 3 null 4 null 5 critical equipment null 6 min on/off null 7 null 8 manual override 0 % 9 null 10 null 11 null 12 null 13 null 14 null 15 null 16 BMS sequence 65 % lowest non-null wins Present_Value 0 % write null to slot 8 → slot 16 wins → Present_Value drops to 65 %

Two things to hold in your head when reading commandable BACnet points: first, that the value you see on the graphic is the resolved value, not necessarily the value the BMS sequence is writing. A sequence writing 65 % at priority 16 can appear to be "broken" because slot 8 still carries a years-old override someone forgot to release. Second, that writing null releases; it doesn't overwrite. Forgetting to release an override is the most common way priority-array logic goes wrong in the field.

To work the resolution rule yourself — type into slots, press × to release one, and watch Present_Value jump to the next winner — the Priority Array resolver is the interactive companion to this section, with the full 16-slot reservation table alongside it.

Who-Is / I-Am — how devices announce themselves

Two unconfirmed services handle device discovery. A client (a BMS, an engineering tool, a JACE) sends a Who-Is request as a network-wide broadcast: any device whose instance number is in this range, please identify yourself. The request can carry a specific range (low and high device-instance bounds) or no range at all — "everybody on the network, speak up."

Every device that hears the broadcast and matches the range responds with I-Am, an unconfirmed broadcast carrying its own device instance number, its maximum APDU length (how big a single message it can accept), its segmentation support, and its vendor ID. The handshake is one-and-done; nothing else is negotiated.

Who-Is broadcast and I-Am replies Four boxes. The leftmost is a client labeled "JACE — Who-Is sender." Three device boxes sit to its right: device:1001, device:1002, and device:2050. An arrow from the client labeled "Who-Is broadcast" reaches all three devices. Each device sends back an "I-Am" reply carrying its device instance number, max APDU, and vendor ID. JACE (discovering) device:1001 VAV-101 device:1002 VAV-102 device:2050 RTU-1 Who-Is (broadcast) I-Am (one each) "who's out there?" → three devices answer with their identities

What this looks like in practice: open EBO's discovery dialog, an N4 Workbench's BACnet network scan, or a tool like Yabe — they fire Who-Is, populate a device list from the I-Am responses, and build the rest of the point browser on top of subsequent ReadProperty calls. A device that doesn't show up in that list almost always has a Who-Is / I-Am problem: the broadcast didn't reach it, or its reply didn't make it back. That story — why discovery sometimes can't cross a router — is the opening of BACnet Networking.

MS/TP vs BACnet/IP — same protocol, different transport

The two BACnet data links you'll meet are MS/TP and BACnet/IP. The object model, the property names, the services, and the priority array are identical between them; only the framing around each message changes.

BACnet MS/TP runs over a two-wire RS-485 bus. Up to about thirty masters share the bus by passing a token: only the controller holding the token may transmit, and once it has sent its message (or used its allotted frame budget) it passes the token to the next master in numeric order. The bus runs at one of a small set of baud rates (typically 38400 or 76800), and the whole bus is one BACnet network sharing a single 16-bit network number. Slow compared to Ethernet, but cheap and robust — most VAV controllers, many small unit controllers, and most MS/TP-only thermostats use it.

BACnet/IP wraps the same BACnet messages inside normal UDP datagrams on a normal Ethernet network. The IANA- registered port is UDP 47808 (which is 0xBAC0 in hex — the source of the "BACnet" port mnemonic). A controller on BACnet/IP is reachable by its IP address and port, identifies itself by the same device instance number, and speaks all the same services. The framing wrapping each message — a small BACnet Virtual Link Layer header, then the BACnet network-layer header, then the actual service — is what's different, along with the broadcast story that BACnet Networking opens with.

MS/TP framing vs BACnet/IP framing Two horizontal framing stacks side by side. The left stack is labeled MS/TP and shows, left to right: Preamble (2 bytes), Frame Type (1 byte), Destination (1 byte), Source (1 byte), Length (2 bytes), a 1-byte header CRC, and a 2-byte data CRC. An MS/TP frame therefore carries two checksums: the header CRC follows the Length field and protects the header, and the data CRC comes last and protects the payload. The common payload — BACnet NPDU plus APDU — rides between the header CRC and the data CRC and is drawn below as shared. The right stack is labeled BACnet/IP and shows five segments: IP header, UDP header (port 47808 = 0xBAC0), BVLL (4 bytes), BACnet NPDU, and APDU. A bracket above each stack highlights that everything except NPDU and APDU is the data-link wrapper; the NPDU and APDU below the bracket are common to both transports. BACnet MS/TP BACnet/IP Preamble 2 B Frame Type Dest Src Length Hdr CRC 1 B Data CRC 2 B IP hdr 20 B UDP hdr port 47808 BVLL 4 B data-link wrapper (changes per transport) data-link wrapper (changes per transport) ↓ identical payload below ↓ NPDU network layer header APDU the actual service protocol logic is the same; only the wrapper changes

The split matters for one practical reason: where a conversation can happen. An MS/TP bus is a closed, low-speed serial loop — fast to commission, but every device on it has to be physically on that loop. BACnet/IP rides whatever Ethernet already exists in the building — a riser, a switch, a VLAN — and can carry a conversation between any two devices on the same IP segment. Crossing an L3 boundary (a router, a different subnet) introduces broadcast rules that take their own page to unpack: that's what BACnet Networking opens with.

The deeper MS/TP story — token rotation, the master/slave token- passing mechanic, Max_Master and Max_Info_Frames, the per-segment device-count and cable-length limits — has its own page: BACnet MS/TP, built around the moments most BMS techs actually touch it — commissioning and bus-fault troubleshooting.

What this page didn't cover

The shape of the protocol is enough to read a BACnet point list, form a Read or Write, recognize a priority-array override, and start a Who-Is discovery. The next question is what happens when the device you want to talk to isn't on the same Ethernet segment you're sending from — the broadcast story, the BBMD and Foreign Device Registration machinery, the three layers of addressing, and the EBO hex blob the BACnet/IP converter tool decodes for you. BACnet Networking is the companion page that walks through each.

Out of scope here, by design: the deep MS/TP token mechanics (token rotation, Max_Master, Max_Info_Frames, baud rates and cable-length budgets — now covered by BACnet MS/TP), segmentation of long messages, alarms and event notifications, schedules and calendars as their own object types, trend logs, and the file / group / loop / notification-class objects. The rest are their own future pages; this one stays on the object model, the services, and the priority array.

← Back to Education