BACnet/IP practical notes
Buffer sizes
Section titled “Buffer sizes”- 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
maxMessageLengthof 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 intomessage. - 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.
Threading and timing
Section titled “Threading and timing”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.
What Who-Is and I-Am look like
Section titled “What Who-Is and I-Am look like”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,broadcasttrue); - one that arrived as Original-Unicast-NPDU is answered with an I-Am unicast to the sender (
81 0A,broadcastfalse).
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().
When nothing arrives
Section titled “When nothing arrives”- Register
BACnetStack_RegisterCallbackLogDebugMessage()in a build withSTACK_OPTION_API_DEBUG_LOGGING. A datagram the stack drops, and everyfalsereturn, 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.