Skip to content
Version 6.1.0
Contact Us

Operating and troubleshooting BACnet/SC

22. Network Port Properties Customers Should Care About

Section titled “22. Network Port Properties Customers Should Care About”

The full property set is in Appendix A. The handful that an operator will most often interact with:

Property ID RW Purpose
SC_Primary_Hub_URI 4194325 R/W Hub URI (writable for field reconfiguration)
SC_Failover_Hub_URI 4194317 R/W Optional failover hub
SC_Hub_Connector_State 4194318 R 0=no, 1=primary, 2=failover
SC_Primary_Hub_Connection_Status 4194324 R TLS/WS error, last connect attempt time
SC_Failover_Hub_Connection_Status 4194316 R Same for failover
SC_Heartbeat_Timeout 4194323 R/W Heartbeat interval (s)
SC_Minimum_Reconnect_Time 4194327 R/W Reconnect backoff floor (s)
SC_Maximum_Reconnect_Time 4194326 R/W Reconnect backoff ceiling (s)
SC_Connect_Wait_Timeout 4194308 R/W Connect-Request response timeout (s)
SC_Direct_Connect_Initiate_Enable 4194313 R/W See note below
SC_Direct_Connect_Accept_Enable 4194309 R/W See note below
Operational_Certificate_File 4194306 R File-object reference (not directly writable - see §16.3)
Issuer_Certificate_Files 511 R Array of two file-object references (not directly writable - see §16.3)
Certificate_Signing_Request_File 509 R CSR output file
Max_BVLC_Length_Accepted 4194304 R/W Default 6144 in a STACK_OPTION_TARGET_FULL build, 1600 otherwise
Max_NPDU_Length_Accepted 4194305 R/W Configured (default 1497)

The stack exposes these via the standard ReadProperty / WriteProperty services. Most writes (URIs, timeouts, the two direct-connect enables) are pending changes that the operator activates with ReinitializeDevice(WARMSTART) or ACTIVATE_CHANGES; see §16.5.

SC_Direct_Connect_Initiate_Enable / SC_Direct_Connect_Accept_Enable are writable. A WriteProperty sets Changes_Pending and the property reads back the pending value; activation applies it to the BACnet/SC data link (§16.5). SC_Hub_Function_Enable is different: the standard requires it to be configurable, not writable, so the stack keeps it read-only and you configure the hub function with BACnetStack_SetBACnetSCHubFunctionConfig (Appendix C.2).

Pitfall: Many BACnet stacks hard-cap property IDs at ~1024. The CAS BACnet Stack handles the 4 194 304+ range. If you proxy or relay BACnet traffic, make sure your code doesn’t reject SC property IDs.

23. Operational Concerns: Heartbeats, Reconnect, and Failover

Section titled “23. Operational Concerns: Heartbeats, Reconnect, and Failover”

Once Connect-Accept has been received, both peers exchange BVLC Heartbeat-Request / Heartbeat-ACK at the configured interval. If a peer fails to ACK within the interval, the connection is considered dead, the WebSocket is closed, and reconnection logic begins.

Two practical consequences:

  1. Don’t block in your CallbackSendMessage or property callbacks. A long blocking call delays heartbeat responses and can cause spurious disconnects.
  2. If your platform sleeps or suspends, the heartbeat timer will fire after wake. Expect a reconnect round on resume.

After a disconnect, the stack waits between SC_Minimum_Reconnect_Time and SC_Maximum_Reconnect_Time seconds (the wait increases in steps from the minimum to the maximum), then asks your application to CallbackInitiateWebsocket again. If the same URI keeps failing, the backoff continues to grow until it reaches the maximum. The reconnect timeout is honoured on every retry, not just the first one.

If a primary URI is configured, the stack will try the primary first. If the primary cannot establish (TLS error, BVLC NAK, timeout), the stack moves to the failover URI. While it is connected to the failover hub, the stack keeps retrying the primary at the reconnect cadence. As soon as the primary connects, the stack disconnects from the failover hub and stays on the primary.

Configure failover by passing the URI in the second pair of arguments to BACnetStack_SetBACnetSCHubConnector.

If your application calls BACnetStack_Reset(), all BACnet/SC configuration and runtime state is cleared (UUID, VMAC, hub connector, direct connect, certificate file-object bindings, session history). After Reset() you can re-add the device, re-add the SC network port, re-load certs, and reconfigure from scratch. Use this on factory reset; do not use it on routine reconnect.

Symptom Likely cause Fix
Tick() returns and connection never starts BACnetStack_SetBACnetSCWebSocketStatus(...,Connected,0) was never called after the WebSocket opened Wire the WS-library “connected” callback to call the notification
The stack never asks for a connection No initiate callback was registered; the stack logs one error (CallbackInitiateWebsocket is not registered, when API debug logging is on) and never attempts a connection Register BACnetStack_RegisterCallbackInitiateWebsocket() (and the disconnect callback) before the first Tick()
The BACnet/SC data link logs an error and does not run A hub function or direct-connect accept role is enabled, but no start-listening callback is registered Register BACnetStack_RegisterCallbackSCStartListening() and BACnetStack_RegisterCallbackSCStopListening()
Enabling the hub function or direct connect fails No accept URI was added first Add at least one wss URI with BACnetStack_AddBACnetSCAcceptUri() before SetBACnetSCHubFunctionConfig or SetBACnetSCDirectConnectConfig
A second BACnetStack_SetBACnetSCUuid() call returns false The UUID can be set once; only BACnetStack_Reset() clears it Set it once at start-up, from persistent storage
State-change log lines show instance 0 No BACnet/SC Network Port object is configured yet for that data link; the instances name the configured port for every transition once it exists Add the BACnet/SC Network Port object before configuring the hub connector
Hub rejects with VMAC-already-in-use Two devices using the same hard-coded VMAC, or the same UUID Give each device its own UUID and configure no VMAC (NULL), so each derives its own. After a refusal the stack chooses a new random VMAC and reconnects; to keep it across restarts, read the Network Port’s MAC_Address and configure that VMAC
Connect-Request never goes out TLS handshake failed silently in your library Enable TLS verbose logging; check that the operational cert chains to one of the issuer certs the hub trusts
Sporadic disconnects at the heartbeat interval Heartbeat-Request not being processed in time Check that no property callback or WS send blocks for more than SC_Heartbeat_Timeout
AtomicReadFile/AtomicWriteFile against a certificate File object is refused (Error, services, optional-functionality-not-supported) The File object was created with BACnetStack_AddObject instead of BACnetStack_AddFileObject, so File_Access_Method/File_Read_Only were never stored Recreate the object with BACnetStack_AddFileObject (§16.1)
Read-only certificate file rejects AtomicWriteFile The File object was created with isWritable=false in AddFileObject, or RegisterCallbackWriteFile returns false Pass isWritable=true if the file should accept updates; the stack answers Error(services, file-access-denied) before your callback runs when File_Read_Only is set
Current_Health (if you enabled it) reports property / invalid-configuration-data for Issuer_Certificate_Files The active binding does not hold exactly two references Annex AB requires two; pass two file-instance values to SetBACnetSCCertificateFileObjects
Sender never receives I-Am replies The receive callback reported the wrong networkPortInstance (or BACNET_NETWORK_PORT_DEFAULT in a multi-transport build, which the stack drops) Set *networkPortInstance to the BACnet/SC Network Port’s instance in the receive callback
Property-ID range error in your relay Code assumes property IDs < 1024 Allow property IDs of 4 194 304 and above (the BACnet/SC Network Port uses IDs from 4 194 304 to 4 194 340)
WebSocket frames are sent but never decoded Frames sent as text instead of binary RFC 6455 opcode 0x2 is required for BACnet/SC
Hub closes immediately after Connect-Accept TLS used a too-old protocol (1.2 or earlier) TLS 1.3 only
Network_Port.command write of generate-csr-file/validate-changes rejected with optional-functionality-not-supported (other refusals of generate-csr-file - pending changes, a host refusal - are in §16.6) generate-csr-file reaches your application only through BACnetStack_RegisterCallbackNetworkPortCommand; with no callback registered it is not supported. validate-changes on a BACnet/SC port needs BACnetStack_RegisterCallbackValidateNetworkPortChanges, because only your application holds the certificates and key to check; without it the write is refused (error class property) Register the Network Port command callback and generate the CSR there (§16.6). For validate-changes, register BACnetStack_RegisterCallbackValidateNetworkPortChanges. Certificate activation always succeeds unconditionally (§16.3 security note)
Staged certificate does not activate Changes_Pending was never set, or ReinitializeDevice was never issued - the stack itself never rejects a staged certificate (see the security note in §16.3) Confirm the AtomicWriteFile actually reached your RegisterCallbackWriteFile and returned true, and that ReinitializeDevice(ACTIVATE_CHANGES/WARMSTART) was issued afterward
Wrote new cert bytes into an existing File object, but nothing seems pending Confirm you rewrote the SAME File object already bound to the Network Port (rebinding to a different File object is a separate operation, via §16.3) A content-only rewrite of a bound File object sets Changes_Pending (§16.5) - if it doesn’t, confirm the write actually reached your RegisterCallbackWriteFile and returned true
WriteProperty to the hub function enable is rejected SC_Hub_Function_Enable is read-only by design; only the two direct-connect enables are writable Configure the hub function with BACnetStack_SetBACnetSCHubFunctionConfig
Device connects to hub but is immediately disconnected Certificate CN or SAN does not match the UUID the hub expects Include the device UUID as a Subject Alternative Name (SAN) in the operational certificate

Q: Can I run BACnet/SC and BACnet/IP on the same device? A: Yes. Add both Network Port objects. Use networkPortInstance in your send/receive callbacks to dispatch to the right transport. The stack already supports multi-network routing.

Q: Does the stack handle TLS for me? A: No. The stack is transport-agnostic. Your WebSocket library does TLS, including all certificate validation. The stack only provides the certificate File objects so they’re visible/writeable through BACnet. It does not hold the certificate bytes either - see §2.7.

Q: How do I rotate certificates without taking the device offline? A: A BACnet client sends new bytes with AtomicWriteFile into the operational/issuer File objects, which your RegisterCallbackWriteFile accepts, and on ReinitializeDevice(ACTIVATE_CHANGES) the stack activates the new cert unconditionally (§16.3 security note) - your TLS library is what actually validates and starts using it. The active SC connections will reconnect at that point and use the new credentials. There is no zero-downtime rotation in the standard.

Q: What about BACnet/SC routing? A: Routing between BACnet/SC and BACnet/IP / MS/TP is not part of this manual’s scope. This manual covers node-only operation.

Q: Direct connect - should I enable it? A: Only if you have a specific need (large file transfers, latency-sensitive trends between two specific devices). Most installations do all peer-to-peer communication through the hub. Remember that enabling it after commissioning, via WriteProperty, requires a ReinitializeDevice(ACTIVATE_CHANGES/WARMSTART) (§16.5).

Q: What’s the smallest device I can run BACnet/SC on? A: TLS 1.3 is the binding constraint. Any platform that can run a TLS 1.3 library such as mbedTLS or wolfSSL can host the stack; size the platform from that library’s own footprint figures for your configuration. See Appendix B for stack-only size estimates.

Q: Does the stack support TLS 1.2 for backwards-compatibility testing? A: The stack has no TLS code; your TLS library does the handshake, and it must refuse anything below TLS 1.3, which Annex AB requires. Some reference implementations expose a non-compliant TLS 1.2 mode for testing - those are not Annex AB compliant and must not be used in production.