BACnet commandable objects use a 16-slot Priority_Array to arbitrate among everything that can write a value. Each WriteProperty request carries a value and a priority (1 is highest, 16 is lowest); the object's Present_Value is taken from the highest non-NULL slot, or from Relinquish_Default if every slot is NULL. The traps: a write at priority 16 silently loses to anything written higher, and a slot keeps its value indefinitely until a NULL is written at that same priority to release it.
Symptoms: Why an Operator Command Won't Stick
Priority array problems rarely look like protocol errors. They look like control logic that has gone deaf. The pattern usually shows up like this:
- Operator override does nothing. A technician writes a setpoint from the front-end. The WriteProperty succeeds (no error response), but Present_Value never changes. A higher-priority slot is already holding a different value.
- Equipment refuses to release. A fan is commanded ON during a smoke test, and weeks later it still won't shut off on the schedule. The smoke-test write at a low priority number is still sitting in its slot because no one wrote NULL to relinquish it.
- Schedule and override fight each other. The schedule writes at one priority; an analytics platform writes at another; a sequence-of-operations block writes at a third. The output bounces depending on which slot wins this minute.
- Two clients write at the same priority. Two writers using the same slot simply overwrite each other—last writer wins, no error, no warning. Many integration bugs trace back to two systems both claiming priority 8.
What the Priority Array Actually Is
The Priority_Array is a property defined by ASHRAE Standard 135 on every commandable object: Analog Output, Binary Output, Multi-State Output, and Analog/Binary/Multi-State Value objects that have been declared commandable. It is an array of exactly 16 entries indexed 1 through 16. Each entry holds either a value of the object's native data type or the special value NULL, which means "this slot is relinquished."
The Present_Value of the object is computed, not stored independently. The BACnet stack walks the array from index 1 (highest priority) to 16 (lowest) and returns the first non-NULL value it finds. If every slot is NULL, the object returns its Relinquish_Default property instead. This algorithm runs on every read and after every write, so changes propagate instantly.
Three more properties are involved and worth knowing by name:
- Present_Value — The computed output. Reading this property gives you the winning value, not the array itself.
- Relinquish_Default — The fallback when all 16 slots are NULL. Configurable at engineering time; this is what the equipment falls back to if every override is released.
- Priority_Array — The full 16-slot array. Read this to see who is currently holding the object, and at what priority.
How to Write a Value (and Release It Later)
A WriteProperty service request to a commandable object accepts an optional priority parameter. The BACnet standard specifies that if a client omits the priority, the write is applied at priority 16 (the lowest). Most BAS supervisors expose this as a numeric field; many also allow writing the literal NULL to relinquish.
- To set a value: WriteProperty to Present_Value with the desired value and a chosen priority. The stack places that value in the matching Priority_Array slot.
- To release that value: WriteProperty to Present_Value with NULL at the same priority you used to set it. This clears that single slot. Writing NULL at a different priority releases a different slot and may have no visible effect.
- To inspect who is holding the object: Read the Priority_Array property directly. The response shows all 16 slots; any non-NULL slot is an active holder.
For Python automation, both BAC0 and BACpypes3 expose a priority parameter on their write methods. Library APIs change across releases, so check the current PyPI page for syntax; our BAC0 BACnet scripting tutorial walks through a first end-to-end write, and the BACpypes documentation covers the lower-level WriteProperty service.
The Recommended Priority Assignments
ASHRAE 135 publishes a recommended priority assignment list (informative, not normative). Vendors and integrators broadly follow it, but it is not enforced by the protocol—any priority can carry any kind of command. The conventional uses:
| Priority | Recommended Use |
|---|---|
| 1 | Manual Life Safety |
| 2 | Automatic Life Safety |
| 3 – 4 | Available |
| 5 | Critical Equipment Control |
| 6 | Minimum On/Off |
| 7 | Available |
| 8 | Manual Operator |
| 9 – 15 | Available |
| 16 | Available (default for writes that omit priority) |
The two life-safety priorities and priority 5 (Critical Equipment Control) are reserved by convention for safety and protection logic—fire alarm interlocks, freeze protection, smoke control, equipment damage prevention. Operator overrides land at priority 8 in most BAS front-ends. Schedules, sequences, and analytics writes typically land somewhere between 9 and 16, with priority 16 being the most common default.
Common Pitfalls
- Writing at priority 16 when something is holding 8. The write succeeds—no error—but the operator-priority slot wins and Present_Value never moves. Always read the Priority_Array before troubleshooting a stuck output.
- Forgetting to relinquish. A temporary override placed at priority 8 stays there until a NULL is written to priority 8. Power cycles do not necessarily clear it; many controllers persist their priority array through reboots.
- Releasing the wrong slot. "Release override" buttons in some BAS front-ends only clear the priority the front-end itself used. If a different system placed a value at a different priority, the front-end button does nothing for that slot. Read the array directly to confirm what released and what didn't.
- Two writers at the same priority. If a schedule and an analytics platform both write priority 14, every write replaces the previous one. There is no mutual exclusion at the protocol level; the most recent write wins until the next one arrives. Establish a priority assignment plan per system at engineering time.
- Schedule objects have their own priority. The Schedule object publishes its writes using the Priority_For_Writing property, which is configurable per schedule. Don't assume schedules write at 16; the engineer may have set them higher to override operator commands, and that is a common source of "why won't this schedule release" tickets.
- Reading Present_Value tells you nothing about the array. Present_Value only shows the winning slot. To see all 16 slots, read the Priority_Array property explicitly. In Wireshark, look for a ReadProperty response carrying the Priority_Array property identifier to see the full picture.
- Non-commandable objects don't have an array. An Analog Input has no Priority_Array—it's a sensor reading, not a command target. Writes to Present_Value on a non-commandable object return an error. Check Object_Type before assuming priority semantics apply.
When to Escalate
Most priority array problems are configuration issues, not protocol bugs. Escalate to the vendor when:
- Reading Priority_Array returns a malformed response or fewer than 16 entries.
- Writing NULL at a specific priority returns success but the slot remains occupied on the next read.
- The controller's Relinquish_Default appears to be ignored when all slots are NULL.
- Two devices on the same network disagree about the priority array on the same object (typically a supervisor caching issue, not the device).
Before contacting support, capture a ReadProperty / WriteProperty exchange showing the unexpected behavior. Confirm the device ID on the responses matches the device you intend to talk to—crossed identities can masquerade as priority bugs. Our duplicate device ID guide covers how that confusion sets in.
Source Attribution
The technical guidance in this entry is informed by the following sources:
- ASHRAE Standard 135 — BACnet—A Data Communication Protocol for Building Automation and Control Networks. Normative reference for commandable objects, the Priority_Array property, the Present_Value computation rule, Relinquish_Default behavior, and the WriteProperty service.
- BACpypes documentation — Reference for WriteProperty service syntax with priority and NULL relinquish patterns in Python.
- BAC0 documentation — Higher-level Python API for BACnet writes, including priority parameter and relinquish helpers.
Additional testing and field validation by SiteConduit.
Was this article helpful?
Related Articles
Need to do this remotely? SiteConduit provides Layer 2 access that preserves BACnet broadcasts — no BBMD needed for remote sessions. Join the waitlist.
SiteConduit Technical Team
Idea Networks Inc.
SiteConduit builds managed remote access for building automation. Our knowledge base is maintained by BAS professionals with hands-on experience deploying and troubleshooting BACnet, Niagara, Modbus, and Facility Explorer systems.