Skip to content
Version 6.1.0
Contact Us

Decoded output reference (XML and JSON)

BACnetStack_DecodeAs turns a raw BACnet message into readable XML or JSON. Use it for logging, debugging, and inspecting the messages a client receives. This chapter shows what that output looks like for each kind of message.

Every example on this page is copied from the stack’s unit tests. Each one shows the bytes the test feeds to the decoder and the output the test checks. Nothing here was written by hand. Where a test checks only part of the output, the example shows only that part and says so. Where no test checks a message at all, the message is listed under Messages not shown instead of being guessed at.

uint32_t BACnetStack_DecodeAs(const uint8_t decodeType,
const char* inMessageRaw, const uint16_t inMessageLength,
char* outMessage, const uint32_t outMessageMaxLength,
const uint8_t packetNetworkType);
Parameter What to pass
decodeType The output format, first: BACnetDecodeType_XML (0) or BACnetDecodeType_JSON (1).
inMessageRaw, inMessageLength The message as it came off the network. For BACnet/IP this is the whole BVLC frame, starting with 0x81. For MS/TP it is the NPDU and APDU, without the MS/TP frame header. For BACnet/SC it is the whole BVLC-SC message as carried in one WebSocket frame, starting with the BVLC function octet.
outMessage, outMessageMaxLength Your output buffer and its size in bytes.
packetNetworkType The transport the bytes came from: 0 for BACnet/IP, 1 for MS/TP, 2 for BACnet/SC, 3 for BACnet/IPv6 (IPv6 builds only; pass the NPDU and APDU without the BVLL header, as for MS/TP). This is not a Network Port instance, so you can decode captures from a transport your own device does not have.

Return value. The number of bytes written to outMessage. Use it as the length of the output. When the buffer has room for one more byte, the stack also writes a terminating '\0' after the output, but do not rely on it.

The function returns 0, and writes nothing, when:

  • decodeType is not one of the two formats, or that format was not compiled into the library;
  • inMessageRaw is NULL or inMessageLength is 0;
  • outMessage is NULL or outMessageMaxLength is 0;
  • packetNetworkType is not a transport compiled into the library;
  • the message cannot be decoded;
  • the output does not fit. The output is never truncated, and the return value does not tell you how much room was needed.

Sizing the buffer. The output is much larger than the message. A ReadPropertyMultiple response can exceed 20 KB. A 64 KB buffer is a safe general-purpose size.

Build options. XML needs STACK_OPTION_DECODE_AS_XML, JSON needs STACK_OPTION_DECODE_AS_JSON, and both need STACK_OPTION_STRING_LITERALS (defining STACK_OPTION_STRING_LITERALS also turns on the XML renderer). XML can be built without JSON, but JSON always comes with XML: defining STACK_OPTION_STRING_LITERALS turns XML on, and a build with JSON but without the string literals stops with an #error. See source/CASBACnetStackOptions.h.

uint8_t frame[] = { 0x81, 0x0a, 0x00, 0x09, 0x01, 0x00, 0x60, 0x7b, 0x01 };
char output[65536];
uint32_t length = BACnetStack_DecodeAs(BACnetDecodeType_JSON, (const char*)frame, sizeof(frame),
output, sizeof(output), 0 /* BACnet/IP */);
if (length == 0) {
/* not decoded, format not compiled in, or the buffer was too small */
}

Both formats render the same tree of decoded objects, so an element in the XML has a key of the same name in the JSON.

The root is BACnetPacket, with a networkType attribute (IP, MSTP, SC or IPv6). Its children are, in order:

  • BVLL (BACnet/IP only): the BVLC function, for example originalUnicastNPDU. A Forwarded-NPDU also carries the originating device’s address.
  • For BACnet/SC, in place of BVLL: an element named after the BVLC-SC message, for example BACnetSCEncapsulatedNPDU or BACnetSCHeartbeatRequest. Its attributes are bvlcFunction and controlFlags (both in hex, such as 0x01) and messageId; its children are OriginatingVirtualAddress, DestinationVirtualAddress, DestinationOptions and DataOptions, each when present. Only an Encapsulated-NPDU is followed by NPDU and the APDU; a control message has neither.
  • NPDU: the network-layer control octet and version, plus the destination and source network and address when present. A network-layer message (such as Reject-Message-To-Network) is a child of NPDU.
  • The APDU, named after the PDU type: ConfirmedRequestPDU, UnconfirmedRequestPDU, SimpleAckPDU, ComplexAckPDU, SegmentAckPDU, ErrorPDU, RejectPDU or AbortPDU (no test on this page shows ConfirmedRequestPDU or SegmentAckPDU; the names are the stack’s). The service, if any, is a child of the APDU (for example IAmRequest, ReadPropertyMultipleAck).

An MS/TP message has no BVLL element. See the I-Am example.

  • The document starts with <?xml version="1.0" encoding="UTF-8"?>, followed on the next line by a version comment, <!-- CAS BACnet Stack v… -->. The JSON has neither.
  • It is indented with tabs, one element per line.
  • Attributes are double-quoted. In every tested example they are in alphabetical order.
  • An element with only attributes is self-closing: <SimpleAckPDU … />.
  • A primitive value has a datatype attribute (its BACnet application tag number), a datatypeLabel attribute (the tag’s name), a machine-readable value attribute (most types), and human-readable element text. See the datatype table.
  • A value inside a constructed type that was encoded with a context tag has a context attribute with the tag number.
  • Text containing & or < is wrapped in <![CDATA[…]]>. A carriage return is written as &#xD;. Bytes that are not valid UTF-8 are replaced with U+FFFD, and control characters that XML does not allow are removed.

These lines are checked by the tests for a BACnet/IP Forwarded-NPDU carrying a Simple-ACK. The test builds the packet in code, so there is no input frame:

<?xml version="1.0" encoding="UTF-8"?>
<!-- CAS BACnet Stack v
<BACnetPacket networkType="IP">
<BVLL function="forwardedNPDU">
<OriginatingDevice>192.168.1.5:47808</OriginatingDevice>
<SimpleAckPDU originalInvokeId="123" serviceAckChoice="writeProperty" />

(Each line is a separate check. The version number after v is not checked.)

  • The output is one line with no whitespace. The JSON on this page is re-indented for reading; the keys, values and order are exactly as the stack emits them.
  • Each element is a key. Its attributes are keys with a leading underscore (_networkType) and its text is _text.
  • Inside an object, _text comes first, then the attributes in alphabetical order, then the child elements.
  • An element that has only text renders as a bare string ("HopCount":"254"). An element with nothing in it renders as {}, never null.
  • Every value is a string, including numbers, except the _value of a Boolean, which is a JSON true or false.
  • A list container (every element whose name starts with ListOf, plus NetworkNumbers and PriorityArray) always holds its entries in an array, even when there is only one. Other repeated elements are an object when there is one and an array when there are several.

The Abort example below, exactly as the stack emits it:

{"BACnetPacket":{"_networkType":"IP","BVLL":{"_function":"originalUnicastNPDU"},"NPDU":{"_control":"0x00","_version":"1"},"AbortPDU":{"_abortReason":"segmentationNotSupported","_originalInvokeId":"75","_server":"1"}}}

Input (BACnet/IP, packetNetworkType 0):

810a00090100714b04000000000000000000

81 0a 00 09 is an Original-Unicast-NPDU of 9 octets, 01 00 the NPDU, and 71 4b 04 an Abort sent by a server, invoke ID 75, reason 4 (segmentation not supported). The test frame carries nine trailing zero octets past the BVLC length; the decoder ignores them.

The test checks this line:

<AbortPDU abortReason="segmentationNotSupported" originalInvokeId="75" server="1" />
{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0x00",
"_version": "1"
},
"AbortPDU": {
"_abortReason": "segmentationNotSupported",
"_originalInvokeId": "75",
"_server": "1"
}
}
}

Input (BACnet/IP):

810a00090100607b01000000000000000000

60 7b 01: a Reject, invoke ID 123, reason 1 (buffer overflow). Nine trailing zero octets.

The test checks this line:

<RejectPDU originalInvokeId="123" rejectReason="bufferOverflow" />
{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0x00",
"_version": "1"
},
"RejectPDU": {
"_originalInvokeId": "123",
"_rejectReason": "bufferOverflow"
}
}
}

Input (BACnet/IP):

810a000d010050c70c9102912a0000000000

50 c7 0c: an Error, invoke ID 199, for a ReadProperty (service 12). 91 02 is the error class (enumerated 2, property) and 91 2a the error code (enumerated 42, invalid array index). Five trailing zero octets.

The test checks these lines:

<ErrorPDU errorChoice="readProperty" originalInvokeId="199">
<ErrorClass datatype="9" datatypeLabel="Enumerated" value="2">property</ErrorClass>
<ErrorCode datatype="9" datatypeLabel="Enumerated" value="42">invalidArrayIndex</ErrorCode>
{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0x00",
"_version": "1"
},
"ErrorPDU": {
"_errorChoice": "readProperty",
"_originalInvokeId": "199",
"Error": {
"ErrorClass": {
"_text": "property",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "2"
},
"ErrorCode": {
"_text": "invalidArrayIndex",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "42"
}
}
}
}
}

Input (BACnet/IP):

810a00090100207b0f000000000000000000

20 7b 0f: a Simple-ACK, invoke ID 123, for a WriteProperty (service 15). Nine trailing zero octets.

The test checks this line:

<SimpleAckPDU originalInvokeId="123" serviceAckChoice="writeProperty" />
{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0x00",
"_version": "1"
},
"SimpleAckPDU": {
"_originalInvokeId": "123",
"_serviceAckChoice": "writeProperty"
}
}
}

A ReadPropertyMultiple response for Network Port 1 with 15 properties. Between them they show most primitive datatypes.

Input (BACnet/IP, 169 octets):

810a00a901080002030d59f9302b0e0c0e0000011e294b4ec40e0000014f294d4e7526005669727475616c20706f7274206f66206e6f646520313630303030303030303031424138354f294f4e91384f29514e104f29674e91004f296f4e8204004f2a018f4e2205c44f2a01a04e114f2a01a44e44000000004f2a01a74e630d59f94f2a01a94e21004f2a01aa4e91004f2a01ab4e91074f2a01e24e91024f2a01e34e233fffff4f1f

01 08 00 02 03 0d 59 f9 is the NPDU, with source network 2 and the 3-octet source address 0D59F9. 30 2b 0e is a Complex-ACK, invoke ID 43, for a ReadPropertyMultiple (service 14).

No test checks the XML for this frame.

{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0x08",
"_version": "1",
"SourceNetwork": "2",
"SourceAddress": {
"_text": "0x0D59F9",
"_length": "3"
}
},
"ComplexAckPDU": {
"_moreFollows": "0",
"_originalInvokeId": "43",
"_segmentedMessage": "0",
"_serviceChoice": "readPropertyMultiple",
"ReadPropertyMultipleAck": {
"ListOfReadAccessResults": {
"_count": "1",
"ReadAccessResult": [
{
"ObjectIdentifier": {
"_text": "networkPort, 1",
"_context": "0",
"_datatype": "12",
"_datatypeLabel": "BACnetObjectIdentifier",
"_objectInstance": "1",
"_objectType": "56",
"_value": "56,1"
},
"ListOfResults": {
"_context": "1",
"_count": "15",
"ReadResult": [
{
"PropertyIdentifier": {
"_text": "objectIdentifier",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "75"
},
"PropertyValue": {
"_context": "4",
"ObjectIdentifier": {
"_text": "networkPort, 1",
"_datatype": "12",
"_datatypeLabel": "BACnetObjectIdentifier",
"_objectInstance": "1",
"_objectType": "56",
"_value": "56,1"
}
}
},
{
"PropertyIdentifier": {
"_text": "objectName",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "77"
},
"PropertyValue": {
"_context": "4",
"CharacterString": {
"_text": "Virtual port of node 160000000001BA85",
"_datatype": "7",
"_datatypeLabel": "CharacterString",
"_encoding": "0",
"_length": "37"
}
}
},
{
"PropertyIdentifier": {
"_text": "objectType",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "79"
},
"PropertyValue": {
"_context": "4",
"ObjectType": {
"_text": "networkPort",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "56"
}
}
},
{
"PropertyIdentifier": {
"_text": "outOfService",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "81"
},
"PropertyValue": {
"_context": "4",
"Boolean": {
"_text": "false",
"_datatype": "1",
"_datatypeLabel": "Boolean",
"_value": false
}
}
},
{
"PropertyIdentifier": {
"_text": "reliability",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "103"
},
"PropertyValue": {
"_context": "4",
"Reliability": {
"_text": "noFaultDetected",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "0"
}
}
},
{
"PropertyIdentifier": {
"_text": "statusFlags",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "111"
},
"PropertyValue": {
"_context": "4",
"BitString": {
"_text": "b0000",
"_datatype": "8",
"_datatypeLabel": "BitString",
"_length": "4",
"_unusedBits": "4"
}
}
},
{
"PropertyIdentifier": {
"_text": "apduLength",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "399"
},
"PropertyValue": {
"_context": "4",
"UnsignedInteger": {
"_text": "1476",
"_datatype": "2",
"_datatypeLabel": "UnsignedInteger",
"_value": "1476"
}
}
},
{
"PropertyIdentifier": {
"_text": "changesPending",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "416"
},
"PropertyValue": {
"_context": "4",
"Boolean": {
"_text": "true",
"_datatype": "1",
"_datatypeLabel": "Boolean",
"_value": true
}
}
},
{
"PropertyIdentifier": {
"_text": "linkSpeed",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "420"
},
"PropertyValue": {
"_context": "4",
"Real": {
"_text": "0.0",
"_datatype": "4",
"_datatypeLabel": "Real",
"_value": "0.0"
}
}
},
{
"PropertyIdentifier": {
"_text": "macAddress",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "423"
},
"PropertyValue": {
"_context": "4",
"OctetString": {
"_text": "0x0D59F9",
"_datatype": "6",
"_datatypeLabel": "OctetString",
"_length": "3"
}
}
},
{
"PropertyIdentifier": {
"_text": "networkNumber",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "425"
},
"PropertyValue": {
"_context": "4",
"UnsignedInteger": {
"_text": "0",
"_datatype": "2",
"_datatypeLabel": "UnsignedInteger",
"_value": "0"
}
}
},
{
"PropertyIdentifier": {
"_text": "networkNumberQuality",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "426"
},
"PropertyValue": {
"_context": "4",
"NetworkNumberQuality": {
"_text": "unknown",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "0"
}
}
},
{
"PropertyIdentifier": {
"_text": "networkType",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "427"
},
"PropertyValue": {
"_context": "4",
"NetworkType": {
"_text": "virtual",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "7"
}
}
},
{
"PropertyIdentifier": {
"_text": "protocolLevel",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "482"
},
"PropertyValue": {
"_context": "4",
"ProtocolLevel": {
"_text": "bacnetApplication",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "2"
}
}
},
{
"PropertyIdentifier": {
"_text": "referencePort",
"_context": "2",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "483"
},
"PropertyValue": {
"_context": "4",
"UnsignedInteger": {
"_text": "4194303",
"_datatype": "2",
"_datatypeLabel": "UnsignedInteger",
"_value": "4194303"
}
}
}
]
}
}
]
}
}
}
}
}

Input (MS/TP, packetNetworkType 1). The bytes are the NPDU and APDU, without the MS/TP frame header:

0128ffff00000106c0a8023bbac0fe1000c40205efeb2205c49103220185

01 28 is the NPDU with a destination and a source: destination network 65535 (global broadcast, address length 0), source network 1 with the 6-octet address C0A8023BBAC0, hop count 254. 10 00 is an Unconfirmed-Request for I-Am, followed by the device identifier (device 389099), the maximum APDU length (1476), segmentation supported (3, none) and the vendor identifier (389).

No test checks the XML for this frame. The same JSON is also pinned, byte for byte, as the shared fixture for the language adapters.

{
"BACnetPacket": {
"_networkType": "MSTP",
"NPDU": {
"_control": "0x28",
"_version": "1",
"DestinationNetwork": "65535",
"DestinationAddress": {
"_length": "0"
},
"HopCount": "254",
"SourceNetwork": "1",
"SourceAddress": {
"_text": "0xC0A8023BBAC0",
"_length": "6"
}
},
"UnconfirmedRequestPDU": {
"_serviceChoice": "iAm",
"IAmRequest": {
"IAmDeviceIdentifier": {
"_text": "device, 389099",
"_datatype": "12",
"_datatypeLabel": "BACnetObjectIdentifier",
"_objectInstance": "389099",
"_objectType": "8",
"_value": "8,389099"
},
"MaxApduLengthAccepted": {
"_text": "1476",
"_datatype": "2",
"_datatypeLabel": "UnsignedInteger",
"_value": "1476"
},
"SegmentationSupported": {
"_text": "noSegmentation",
"_datatype": "9",
"_datatypeLabel": "Enumerated",
"_value": "3"
},
"VendorId": {
"_text": "389",
"_datatype": "2",
"_datatypeLabel": "UnsignedInteger",
"_value": "389"
}
}
}
}
}

The tests below decode a whole frame but check only parts of the JSON. The parts are shown as the tests check them.

Object_Type of Color 1. Input (BACnet/IP):

810a0014010030810c0c0fc00001194f3e913f3f

30 81 0c is a Complex-ACK, invoke ID 129, for a ReadProperty (service 12), followed by the object (Color 1), the property (79, Object_Type) and the value (enumerated 63). The test checks that the JSON contains these two fragments:

"_text":"color"
"_value":"63"

Present_Value of Group 1 (a list of ReadAccessResult). Input (BACnet/IP):

810a0035010030610c0c02c0000119553e0c008000011e29554e4441ccafb54f29674e91004f1f0c014000011e29554e91004f1f3f

The value holds two results: Analog Value 1 (Present_Value, Real 0x41CCAFB5, and Reliability) and Binary Value 1 (Present_Value). The test checks that the JSON contains no UnknownAPDU and contains these fragments:

"ReadAccessResult":[
"_text":"analogValue, 1"
"_text":"binaryValue, 1"
"PropertyIdentifier":{"_text":"presentValue"
"_value":"103"
"25.585794"

Partly checked: ReadPropertyMultiple-ACK with an out-of-range Date

Section titled “Partly checked: ReadPropertyMultiple-ACK with an out-of-range Date”

A 412-octet ReadPropertyMultiple response from a Binary Input with 39 properties, three of which carry a Date whose month, day and weekday are 0. Input (BACnet/IP):

810a019c010030290e0c00c000011e294b4ec400c000014f294d4e75120042696e617279496e7075744f626a6563744f294f4e91034f29554e91004f291c4e71004f291f4e71004f296f4e8204004f29244e91004f29674e91004f29514e104f29544e91004f292e4e71004f29044e71004f29104ea400000000b4000000424f290f4e21004f29734ea400000000b4000000424f29214e21004f29724ea400000000b4000000424f29714e21004f29114e21004f29064e91004f29234e8205004f29004e8205004f29484e91004f29824e0cffffffff0cffffffff0cffffffff4f2a015f4e7100710071004f2a01604e7100710071004f2a01614e104f2a01634e0cffffffff19004f2a01624e104f2a01644e21004f2a01654e104f2a01734e9155911c911f916f9124916791519154912e91049110910f917391219172917191119106912391009148918292015f9201609201619201639201629201649201659201839201f29201f59201e69201e591a84f2a01834e004f2a01f24e91004f2a01f54e830000004f2a01e64e4f2a01e54e71004f29a84e71004f1f

The test checks these JSON fragments. The out-of-range text appears three times, once for each Date:

"ListOfResults":{"_context":"1","_count":"39"
"CharacterString":{"_text":"BinaryInputObject"
"_text":"presentValue","_context":"2","_datatype":"9","_datatypeLabel":"Enumerated","_value":"85"},"PropertyValue":{"_context":"4","BinaryPV":{"_text":"inactive"
Out of range: 0/0/1900, weekday 0
"_day":"0","_month":"0","_outOfRange":"true","_weekday":"0","_year":"0"
00:00:00.66

A Date that is out of range is not an error: the rest of the message still renders, and the Date carries outOfRange="true" with its raw octets.

Partly checked: ReadRange-ACK for a Trend Log Multiple

Section titled “Partly checked: ReadRange-ACK for a Trend Log Multiple”

Input (BACnet/IP, 53 octets): the first record of a Trend Log Multiple Log_Buffer (two Reals and an Unsigned).

810A0035010030531A0C06C0000119833A05C049015E0EA47E091804B404341C000F1E1E1C41A7BE5E1C424F275139011F1F5F6901

The test checks only that the JSON contains the element names ItemData, LogData, RealValue and UnsignedValue, and does not contain UnknownAPDU.

Input (BACnet/IP):

810a001401a0000106c0a8023bbac0ff03040004

01 a0 is an NPDU that carries a network-layer message and a destination: network 1, address C0A8023BBAC0, hop count 255. 03 04 00 04 is Reject-Message-To-Network, reason 4 (message too long to be routed), for network 4.

No test checks the XML for this frame. The message is a child of NPDU, and there is no APDU.

{
"BACnetPacket": {
"_networkType": "IP",
"BVLL": {
"_function": "originalUnicastNPDU"
},
"NPDU": {
"_control": "0xA0",
"_version": "1",
"DestinationNetwork": "1",
"DestinationAddress": {
"_text": "0xC0A8023BBAC0",
"_length": "6"
},
"HopCount": "255",
"RejectMessageToNetwork": {
"_networkNumber": "4",
"_rejectionReason": "4",
"_rejectionReasonText": "messageTooLongToBeRouted"
}
}
}
}

Built in code: I-Am-Router-To-Network and I-Could-Be-Router-To-Network

Section titled “Built in code: I-Am-Router-To-Network and I-Could-Be-Router-To-Network”

These tests render a message object built in code, not a decoded frame, so there is no input. They show how the network-layer fields render.

An I-Am-Router-To-Network for network 7. Its network list is an array even with one entry:

"NetworkNumber":["7"]

An I-Could-Be-Router-To-Network for network 2 with performance index 5:

networkNumber="2"
performanceIndex="5"
"_networkNumber":"2"
"_performanceIndex":"5"

These frames carry no NPDU. The tests check fragments of the output.

Read-Broadcast-Distribution-Table-Ack, one entry (192.168.1.1:47808, mask 255.255.255.255):

8103000EC0A80101BAC0FFFFFFFF

JSON contains readBroadcastDistributionTableAck, BDTEntry, 192.168.1.1:47808 and "_broadcastdistributionmask":"FFFFFFFF". The XML contains readBroadcastDistributionTableAck, BDTEntry and 192.168.1.1:47808.

With two entries (192.168.1.1 and 192.168.2.1, the second with mask 255.255.255.0):

81030018C0A80101BAC0FFFFFFFFC0A80201BAC0FFFFFF00

JSON contains "BDTEntry":[, the two addresses in order, and "_broadcastdistributionmask":"FFFFFF00". With one entry, BDTEntry is an object, not an array.

Write-Broadcast-Distribution-Table, one entry (192.168.1.3:47808, mask 255.255.255.0):

8101000EC0A80103BAC0FFFFFF00

JSON contains writeBroadcastDistributionTable, BDTEntry, 192.168.1.3:47808 and "_broadcastdistributionmask":"FFFFFF00".

Read-Foreign-Device-Table-Ack, one entry (192.168.1.2:47808, time-to-live 60, 64 seconds remaining):

8107000EC0A80102BAC0003C0040

JSON contains readForeignDeviceTableAck, FDTEntry, 192.168.1.2:47808, "_timetolive":"60" and "_remainingtimetolive":"64".

BVLC-Result with result code 0x0020 (Read-Broadcast-Distribution-Table NAK):

810000060020

JSON contains ReadBroadcastDistributionTableNak.

Every primitive renders the same way wherever it appears. The XML has datatype, datatypeLabel and (for most types) value attributes plus element text; the JSON has the same as _datatype, _datatypeLabel, _value and _text.

Examples marked (frame) come from the ReadPropertyMultiple example or another decoded frame above. The others come from tests that render a single value built in code; where those tests check fragments, the fragments are shown.

datatype datatypeLabel XML JSON
0 Null not shown not shown
1 Boolean value="true" / value="false" {"Boolean":{"_text":"true","_datatype":"1","_datatypeLabel":"Boolean","_value":true}}
2 UnsignedInteger <timeToLive context="1" datatype="2" datatypeLabel="UnsignedInteger" value="650">650</timeToLive> (frame) "UnsignedInteger":{"_text":"1476","_datatype":"2","_datatypeLabel":"UnsignedInteger","_value":"1476"}
3 SignedInteger element <SignedInteger …> with datatypeLabel="SignedInteger" not shown
4 Real datatype="4", datatypeLabel="Real", value="21.5"; 50 renders value="50.0" and text 50.0 {"Real":{"_text":"50.0","_datatype":"4","_datatypeLabel":"Real","_value":"50.0"}}
5 Double 2 renders value="2.0" not shown
6 OctetString <bacnetIpAddress context="0" datatype="6" datatypeLabel="OctetString" length="6">0xC0A8010ABAC0</bacnetIpAddress> (frame) "OctetString":{"_text":"0x0D59F9","_datatype":"6","_datatypeLabel":"OctetString","_length":"3"}
7 CharacterString AT&T renders as <![CDATA[AT&T]]> (frame) "CharacterString":{"_text":"Virtual port of node 160000000001BA85","_datatype":"7","_datatypeLabel":"CharacterString","_encoding":"0","_length":"37"}
8 BitString not shown (frame) "BitString":{"_text":"b0000","_datatype":"8","_datatypeLabel":"BitString","_length":"4","_unusedBits":"4"}
9 Enumerated (frame) <ErrorClass datatype="9" datatypeLabel="Enumerated" value="2">property</ErrorClass> (frame) "ErrorClass":{"_text":"property","_datatype":"9","_datatypeLabel":"Enumerated","_value":"2"}
10 Date year="126" and yearFull="2026" for 2026; an unspecified year renders year="255" and no yearFull (frame, out of range) "_day":"0","_month":"0","_outOfRange":"true","_weekday":"0","_year":"0"
11 Time datatypeLabel="Time", hundredthSeconds="0"; text 07:05:59.00, 16:17:50.12 (frame) text 00:00:00.66
12 BACnetObjectIdentifier objectType="56", objectInstance="1", value="56,1" (frame) "ObjectIdentifier":{"_text":"networkPort, 1","_datatype":"12","_datatypeLabel":"BACnetObjectIdentifier","_objectInstance":"1","_objectType":"56","_value":"56,1"}

Notes on the values:

  • Date year is the octet on the wire, years since 1900. Add 1900, or read yearFull.
  • Real uses the fewest digits, six or more, that read back to the same value: 1234.567, not 1234.57. A whole number keeps .0.
  • Time text is always HH:MM:SS.hh, zero-padded.
  • CharacterString text in JSON is escaped as JSON requires: the string a"b\c followed by a newline renders as "_text":"a\"b\\c\n".

A constructed value is an element containing its members. A member encoded with a context tag has a context attribute. A Foreign Device Table entry, decoded from bytes in the test (0D 06 C0A8010ABAC0 1A 028A 2A 012C) and rendered on its own, with the test’s line breaks and tabs removed:

<FDTEntry><bacnetIpAddress context="0" datatype="6" datatypeLabel="OctetString" length="6">0xC0A8010ABAC0</bacnetIpAddress><timeToLive context="1" datatype="2" datatypeLabel="UnsignedInteger" value="650">650</timeToLive><remainingTimeToLive context="2" datatype="2" datatypeLabel="UnsignedInteger" value="300">300</remainingTimeToLive></FDTEntry>

In JSON, see ReadAccessResult and ListOfResults in the ReadPropertyMultiple example: each list has a _count, and its entries are an array.

An element with nothing in it is {}. A Who-Is with no range, built in code and rendered on its own:

{"WhoIsRequest":{}}

No unit test checks the decoded output of these messages, so this chapter does not show them. They are decoded and rendered with the same conventions as the examples above.

  • Confirmed-Request PDU: no test checks the rendering of any confirmed request, so none of the request side of these services is shown: ReadProperty, ReadPropertyMultiple, WriteProperty, WritePropertyMultiple, SubscribeCOV, AtomicReadFile, AtomicWriteFile, DeviceCommunicationControl, ReinitializeDevice, ReadRange.
  • Segment-ACK PDU.
  • Unconfirmed services: Who-Is decoded from a frame, Who-Has, I-Have, TimeSynchronization.
  • COV notifications, confirmed and unconfirmed.
  • Event notifications. A test renders a ConfirmedEventNotification that the stack generates itself and checks only that the JSON contains ConfirmedEventNotificationRequest and ChangeOfReliability.
  • AtomicReadFile-ACK and AtomicWriteFile-ACK.
  • ReadRange-ACK in full (only the element names are checked, above).
  • Network-layer messages: Who-Is-Router-To-Network decoded from a frame, and I-Am-Router-To-Network decoded from a frame (only the built-in-code fragment above).
  • BACnet/SC messages.
  • Datatypes: Null, and the JSON of SignedInteger, Double and the XML of BitString.