Bacnet Apdu Encoding

Decoding BACnet Messages: Understanding APDU Encoding

Imagine sending a letter with a strict format: a header indicating the letter type, a section identifying the recipient, the actual message content, and a footer with delivery instructions. If the recipient doesn’t follow the same format, they can’t understand your letter. BACnet messages work exactly this way—they use a structured format called Application Protocol Data Units (APDUs) to ensure devices can communicate regardless of manufacturer.

In this article, we’ll decode BACnet APDU structure, explain the Tag-Length-Value (TLV) encoding method, compare context-specific and application tags, and walk through the step-by-step encoding of a simple `ReadProperty` request and response.

What Is a BACnet APDU?

An APDU (Application Protocol Data Unit) is the “letter” that BACnet devices send to each other. It’s the application layer message that contains the actual command, data, or response.

Letter Analogy:

  • Service Data: The actual content of the letter

APDU Structure: The Parts of the Letter

A BACnet APDU has a fixed structure, similar to a formal business letter:

| APDU Component | Letter Analogy | Description |

|———————|——————–|——————|

| APDU Type | Letter Type (Request, Response) | Identifies the APDU as a request, response, or notification |

| Service Choice | Subject Line | Specifies the BACnet service (ReadProperty, WriteProperty, etc.) |

| Service Data | Letter Body | Contains the actual data for the service (addresses, values, etc.) |

Tag-Length-Value (TLV): The Language of APDUs

BACnet uses Tag-Length-Value (TLV) encoding to structure the Service Data portion of APDUs. Think of TLV as the “grammar” of BACnet messages—each piece of data follows the same pattern.

What Is TLV Encoding?

TLV breaks down each data element into three parts:

1. Tag: The “label” that identifies what the data represents (e.g., Device ID, Property ID)

2. Length: The “size” that tells how many bytes follow

3. Value: The actual “data” (e.g., “Device 123”, “Temperature 72°F”)

TLV Analogy: Addressing an Envelope

“`

[Tag: “To”] [Length: 10 bytes] [Value: “John Doe”]

[Tag: “From”] [Length: 15 bytes] [Value: “Jane Smith”]

[Tag: “Subject”] [Length: 20 bytes] [Value: “Meeting Agenda”]

[Tag: “Body”] [Length: 500 bytes] [Value: “Meeting at 2 PM…”]

“`

This structure ensures the recipient knows exactly what each part of the message means, even if they don’t know the sender.

BACnet Tag Types: Context-Specific vs Application

BACnet uses two main tag types, each serving a different purpose:

1. Context-Specific Tags: “Private Letters”

Context-specific tags are like private letters—they only make sense within a specific context (service). They use small numbers (0-127) and are only meaningful for the current service.

Example: Tag 0 might mean “Device ID” in a `ReadProperty` service, but “Temperature” in a `WriteProperty` service.

Key Features:

  • Used for most BACnet service parameters

2. Application Tags: “Public Letters”

Application tags are like public letters—they have a universal meaning across all BACnet services. They identify standard BACnet data types.

Example: Tag 1 means “Boolean” in any service, Tag 2 means “Unsigned Integer”, Tag 3 means “Real” (float).

Key Features:

  • Have a standard list defined in the BACnet specification

BACnet Application Tag List

Here are the most common BACnet application tags:

| Tag | Data Type | Example |

|———|—————|————-|

| 0 | Null | No value |

| 1 | Boolean | True/False |

| 2 | Unsigned Integer | 123 |

| 3 | Real | 72.5 |

| 4 | Double | 3.1415926535 |

| 5 | Octet String | Binary data |

| 6 | Character String | “Hello” |

| 7 | Bit String | 0b1010 |

| 8 | Enumerated | Specific values like “Heating”, “Cooling” |

| 9 | Date | 2026-01-04 |

| 10 | Time | 14:30:00 |

Step-by-Step: Encoding a ReadProperty Request

Now, let’s walk through the encoding of a simple `ReadProperty` request. This is one of the most common BACnet services, used to read a property from a device.

Request Scenario

  • Property ID: Present Value (current temperature)

Step 1: APDU Type and Service Choice

First, we encode the APDU Type and Service Choice:

| Component | Hex Value | Description |

|—————|—————|—————–|

| APDU Type | 0x00 | Confirmed Request (device expects a response) |

| Service Choice | 0x0C | ReadProperty service |

Step 2: Invoke ID

Next, we add an Invoke ID to match requests with responses (like a letter reference number):

| Component | Hex Value | Description |

|—————|—————|—————–|

| Invoke ID | 0x01 | Unique identifier for this request/response pair |

Step 3: Priority and Context-Specific Tags

Now, we add priority (default is 0) and context-specific tags for service parameters:

| Component | Hex Value | Description |

|—————|—————|—————–|

| Priority | 0x00 | Default priority (highest priority is 16) |

| Tag 0 (Object Type) | 0x20 | Context tag 0, application tag 8 (Enumerated: Analog Input = 0) |

| Object Type Value | 0x00 | Analog Input (enumerated value 0) |

| Tag 1 (Object Instance) | 0x21 | Context tag 1, application tag 2 (Unsigned Integer) |

| Object Instance Value | 0x2D | Object Instance 45 (0x2D = 45 decimal) |

| Tag 2 (Property ID) | 0x22 | Context tag 2, application tag 8 (Enumerated: Present Value = 85) |

| Property ID Value | 0x55 | Present Value (enumerated value 85) |

Step 4: Complete Encoded Request

Putting it all together, the complete `ReadProperty` request in hex is:

“`

00 0C 01 00 20 00 21 2D 22 55

“`

Breakdown:

  • `55`: Property ID Value (Present Value = 85)

Step-by-Step: Encoding a ReadProperty Response

Now, let’s encode the response to our `ReadProperty` request. The response will include the temperature value from the sensor.

Response Scenario

  • Property Value: 72.5°F (represented as a Real number)

Step 1: APDU Type and Service Choice

First, we encode the APDU Type and Service Choice for the response:

| Component | Hex Value | Description |

|—————|—————|—————–|

| APDU Type | 0x01 | Simple ACK (response to confirmed request) |

| Service Choice | 0x0C | ReadProperty service (matches request) |

Step 2: Invoke ID (Matching Request)

We use the same Invoke ID as the request to match them together:

| Component | Hex Value | Description |

|—————|—————|—————–|

| Invoke ID | 0x01 | Matches the request’s Invoke ID |

Step 3: Service Result and Property Value

Next, we encode the service result (success) and the actual property value:

| Component | Hex Value | Description |

|—————|—————|—————–|

| Result Code | 0x00 00 | Success (no error) |

| Context Tag 0 (Property Value) | 0x40 | Context tag 0, application tag 3 (Real)

| Property Value | 0x42 F0 00 00 | Temperature 72.5°F as a Real (0x42F00000 = 72.5 in IEEE 754 floating point) |

Step 4: Complete Encoded Response

Putting it all together, the complete `ReadProperty` response in hex is:

“`

01 0C 01 00 00 40 42 F0 00 00

“`

Breakdown:

  • `42 F0 00 00`: Property Value (72.5°F as Real)

Why Understanding APDU Encoding Matters

Understanding BACnet APDU encoding is like knowing how to read a letter—it lets you understand exactly what devices are saying to each other. This knowledge is valuable for:

1. Troubleshooting: “Why isn’t my device responding?”

  • You can debug communication issues between devices

2. Custom Development: “Building my own BACnet device”

  • This knowledge is essential for implementing BACnet stacks

3. Security: “Is someone tampering with my BACnet messages?”

  • You can implement proper authentication and encryption

4. Optimization: “How can I make my messages smaller?”

  • Smaller messages mean faster communication and less network traffic

Tools for Working with BACnet APDUs

Several tools can help you decode and analyze BACnet APDUs:

1. BACnet Protocol Analyzers

  • Yabe (Yet Another BACnet Explorer): Open-source BACnet client/analyzer

2. BACnet Stack Libraries

  • pymodbus: Python library for BACnet (and Modbus)

3. Online Decoders

  • Useful for quick analysis without installing software

Conclusion: Speaking BACnet’s Secret Language

Decoding BACnet messages is like learning a secret language. Once you understand the APDU structure, TLV encoding, and tag types, you can “listen” to what your building devices are saying to each other.

Whether you’re troubleshooting communication issues, developing custom devices, or optimizing your BACnet network, understanding APDU encoding gives you a deeper insight into how your building automation system works. It’s the key to unlocking the full potential of BACnet.

So, the next time you see a hex string representing a BACnet message, remember—it’s not just random bytes. It’s a structured letter, with tags, length, and values that tell a story about your building’s operations. And now, you know how to read it.