Skip to content
Version 6.1.0
Contact Us

BACnet/IP practical notes

  • The largest NPDU on BACnet/IP is 1497 octets. The BVLL header in front of it is 4 octets for an Original-Unicast-NPDU or Original-Broadcast-NPDU and 10 octets for a Forwarded-NPDU, so a BACnet/IP datagram is at most 1507 octets.
  • The stack offers the receive callback a maxMessageLength of 1529 octets in a build without BACnet/SC, and 6144 octets in a build with it. Both are large enough for any BACnet/IP datagram, so you can receive straight into message.
  • The connection-string buffers are 255 octets, far more than the 6 that BACnet/IP needs.
  • An IPv4 Network Port reports APDU_Length 1476. Longer replies are segmented if the device has segmentation enabled.

By default the stack is single-threaded. Call BACnetStack_Tick() and every other function, including those your transport code calls, from one thread (a build with STACK_OPTION_THREAD_SAFE lifts this; see Stack lifecycle). If your socket is read on another thread, queue the datagrams and hand them to the stack from the receive callback, one per tick.

Because the receive callback returns one datagram per tick, the tick rate limits how many datagrams per second the device can take in. A tick every 10 to 100 ms suits a typical device. If datagrams arrive faster than you tick, the socket’s receive buffer fills and the operating system drops the excess. Tick faster, or call BACnetStack_Tick() again straight away while it returns true.

Every BACnet/IP datagram starts with a 4-octet BVLL header: type 0x81, a function code, and the total length in two octets, most significant first. The function codes you will see most are:

Function Code Sent as
Original-Unicast-NPDU 0x0A a unicast to one device
Original-Broadcast-NPDU 0x0B a local broadcast
Distribute-Broadcast-To-Network 0x09 a foreign device’s broadcast, unicast to its BBMD
Forwarded-NPDU 0x04 a broadcast relayed by a BBMD, with the originator’s 6-byte address after the header

A Who-Is sent with BACnetStack_SendWhoIs() and broadcast true leaves as 81 0B ... with broadcast true in your send callback. When the stack answers a Who-Is:

  • one that arrived as Original-Broadcast-NPDU or as Forwarded-NPDU is answered with an I-Am sent as a local broadcast (81 0B, broadcast true);
  • one that arrived as Original-Unicast-NPDU is answered with an I-Am unicast to the sender (81 0A, broadcast false).

On a registered foreign device, every local broadcast in this list leaves as 81 09 to the BBMD instead, with broadcast false.

To see the decoded content of a datagram, pass it to BACnetStack_DecodeAs().

  • Register BACnetStack_RegisterCallbackLogDebugMessage() in a build with STACK_OPTION_API_DEBUG_LOGGING. A datagram the stack drops, and every false return, is explained only there.
  • Check that the receive callback reports a source length of 6 and, in a multi-transport build, a real networkPortInstance.
  • Check that the socket is bound to the BACnet UDP port on all interfaces (INADDR_ANY), not to one unicast address: some operating systems do not deliver broadcasts to a socket bound to a unicast address.
  • Check the host firewall for UDP 47808.
  • If the device and the client are on different IP subnets, broadcasts do not cross the router between them. Use a BBMD or register as a foreign device; see BBMD and foreign devices.
  • After BACnetStack_Reset(), register the send and receive callbacks again and re-add the device. Your socket is untouched.