BACnet/SC reference
Reference material for the BACnet/SC chapter. The BACnet/SC terms it uses (VMAC, device UUID, hub connector, hub function, direct connect, CSR, mTLS) are defined in the manual’s Glossary.
Appendix A - BACnet/SC Network Port Property Reference
Section titled “Appendix A - BACnet/SC Network Port Property Reference”Properties marked R/W support WriteProperty. Writes typically become pending and are activated by ReinitializeDevice(WARMSTART | ACTIVATE_CHANGES). Property identifiers are listed in the Enumerations reference.
Object_Name is read-only on a Network Port by default, as on every object type; make it writable with
BACnetStack_SetPropertyWritable if your product needs that.
A.1 Mandatory properties
Section titled “A.1 Mandatory properties”| Property | ID | Datatype | RW | Notes |
|---|---|---|---|---|
| Object_Identifier | 75 | ObjectID | R | network-port,instance |
| Object_Name | 77 | CharString | R | Human-readable; not writable by default (see above) |
| Object_Type | 79 | Enum | R | network-port (56) |
| Status_Flags | 111 | BitString | R | in-alarm, fault, overridden, out-of-service |
| Reliability | 103 | Enum | R | no-fault-detected (0) etc. |
| Out_Of_Service | 81 | Boolean | R/W | Standard out-of-service flag; enabled and writable on an SC Network Port |
| Network_Type | 427 | Enum | R | secure-connect (11) |
| Protocol_Level | 482 | Enum | R | bacnet-application (2) |
| Reference_Port | 483 | Unsigned | R | Lower-layer Network Port reference |
| Network_Number | 425 | Unsigned16 | R | BACnet network number; not writable by default |
| Network_Number_Quality | 426 | Enum | R | unknown(0) / learned(1) / learned-configured(2) / configured(3) |
| Changes_Pending | 416 | Boolean | R | True if pending writes are present |
| MAC_Address | 423 | OctetString | R | VMAC |
| APDU_Length | 399 | Unsigned | R | Negotiated max APDU |
A.2 BACnet/SC mandatory properties
Section titled “A.2 BACnet/SC mandatory properties”The standard makes these mandatory. The stack enables SC_Primary_Hub_Connection_Status itself, and
SC_Failover_Hub_Connection_Status while a failover hub URI is configured (it re-checks whenever the SC hub connector
settings change). It does not enable Current_Health: the application turns that on with BACnetStack_SetPropertyEnabled.
| Property | ID | Datatype | RW | Notes |
|---|---|---|---|---|
| Max_BVLC_Length_Accepted | 4194304 | Unsigned16 | R/W | Default 6144 in a STACK_OPTION_TARGET_FULL build, 1600 otherwise |
| Max_NPDU_Length_Accepted | 4194305 | Unsigned16 | R/W | Default 1497 |
| SC_Connect_Wait_Timeout | 4194308 | Unsigned16 | R/W | Connect-Request timeout |
| SC_Disconnect_Wait_Timeout | 4194314 | Unsigned16 | R/W | Disconnect-ACK timeout |
| SC_Heartbeat_Timeout | 4194323 | Unsigned16 | R/W | Heartbeat interval |
| SC_Minimum_Reconnect_Time | 4194327 | Unsigned16 | R/W | Backoff floor |
| SC_Maximum_Reconnect_Time | 4194326 | Unsigned16 | R/W | Backoff ceiling |
| SC_Primary_Hub_URI | 4194325 | CharString | R/W | wss://... |
| SC_Failover_Hub_URI | 4194317 | CharString | R/W | wss://... or empty |
| SC_Primary_Hub_Connection_Status | 4194324 | Constructed | R | error, last connect timestamp |
| SC_Failover_Hub_Connection_Status | 4194316 | Constructed | R | Same for failover |
| SC_Hub_Connector_State | 4194318 | Enum | R | 0=no, 1=primary, 2=failover |
| Operational_Certificate_File | 4194306 | ObjectID | R | Reference to File; not directly writable (§16.3) |
| Issuer_Certificate_Files | 511 | Array of ObjectID | R | Exactly two; not directly writable (§16.3) |
| Certificate_Signing_Request_File | 509 | ObjectID | R | Reference to File |
A.3 Optional properties (selected)
Section titled “A.3 Optional properties (selected)”| Property | ID | RW | Used when |
|---|---|---|---|
| Device_Address_Proxy_Enable | 4194338 | R/W | Present while the device’s physical router is enabled, which is when it proxies device addresses (cl. 19.9) |
| Device_Address_Proxy_Timeout | 4194340 | R/W | As above |
| Link_Speed | 420 | R | Effective WebSocket link speed (Real) |
| Routing_Table | 428 | R | Routing-aware deployments |
| SC_Failed_Connection_Requests | 4194315 | R | Hub function or direct-accept diagnostics |
| SC_Hub_Function_Enable | 4194322 | R | Cl. 12.56.90 requires it to be configurable, not writable - configured via BACnetStack_SetBACnetSCHubFunctionConfig, not WriteProperty |
| SC_Hub_Function_Accept_URIs | 4194319 | R | Only when hub function enabled |
| SC_Hub_Function_Binding | 4194320 | R | Only when hub function enabled. Every configured hub-function accept URI, comma-separated, whatever the listener’s state |
| SC_Hub_Function_Connection_Status | 4194321 | R | Constructed, list. Only when hub function enabled |
| SC_Direct_Connect_Initiate_Enable | 4194313 | R/W | See §16.5/§22 |
| SC_Direct_Connect_Accept_Enable | 4194309 | R/W | See §16.5/§22 |
| SC_Direct_Connect_Accept_URIs | 4194310 | R | Only when direct connect accept enabled |
| SC_Direct_Connect_Binding | 4194311 | R | Only when direct connect accept enabled. Every configured direct-connect accept URI, comma-separated; never the URI of a peer this node connected to |
| SC_Direct_Connect_Connection_Status | 4194312 | R | Constructed, list. Only when direct connect enabled |
The full list of property identifiers is in the Enumerations reference.
SC_Direct_Connect_Connection_Status and SC_Hub_Function_Connection_Status keep a connection’s entry after it ends, with its final state, its Disconnect Timestamp (for a failed initiated attempt, FAILED_TO_CONNECT with the Connect Timestamp of the attempt) and, when it ended with an error, its Error and Error Details. A live connection stays listed, as CONNECTED, while it disconnects, until its finished entry replaces it. A peer that connects again replaces its own entry. Each list keeps at most 8 finished entries, dropping the oldest first; entries for live connections are never dropped.
A direct connection this port is initiating is listed from the start of the attempt, while its WebSocket opens and while it waits for the Connect-Accept: NOT_CONNECTED (the connection-state list has no “attempting” value), with the URI it is using, both timestamps unspecified and no Peer VMAC or UUID. The same entry becomes CONNECTED when the peer accepts or, if the attempt fails, is recorded as FAILED_TO_CONNECT; an attempt the port abandons itself (for example, initiation switched off) is not recorded. While a new attempt to a peer is in progress, that peer’s earlier finished entry is not listed beside it.
Appendix B - Compile Flag and Size Estimates
Section titled “Appendix B - Compile Flag and Size Estimates”| Configuration | Approx. flash delta | Approx. RAM delta | Notes |
|---|---|---|---|
| BACnet/SC disabled | 0 KB | 0 KB | Baseline |
| Hub connector only (node) | +40–50 KB | +8–12 KB | Recommended for server devices |
| + Hub function (runtime-enabled) | +15–20 KB | +4–8 KB + per-conn | Adds listener / forwarder |
| + Direct connect (runtime-enabled) | +10–15 KB | +2–4 KB + per-conn | Adds peer-to-peer paths |
Per-connection RAM overhead is roughly 1–2 KB (frame buffer, state, timers) for each active SC connection.
These are rough estimates, not measurements of this release. Measure on your own target compiler and link configuration.
B.1 Required vs. optional flags
Section titled “B.1 Required vs. optional flags”Required for any BACnet/SC integration (in source/CIBuildSettings.h or as CMake options; see
Build presets):
#define STACK_OPTION_DATA_LINK_LAYER_SC#define STACK_OPTION_OBJECT_TYPE_NETWORK_PORT // also turned on automatically by SC#define STACK_OPTION_OBJECT_TYPE_FILE#define STACK_OPTION_NM_SCCM_A // turns on AtomicReadFile/AtomicWriteFile and ReinitializeDevice#define STACK_OPTION_DATA_LINK_LAYER_IPV4 // currently required by shared utility codeSTACK_OPTION_NM_SCCM_A (or STACK_OPTION_ENABLE_ALL_BIBBS, which includes it in an SC build) turns on the
AtomicReadFile/AtomicWriteFile support. Without it BACnetStack_AddFileObject returns false and the read and
write file callbacks are never called, so the certificate File objects cannot be served. It also turns on
ReinitializeDevice, which a client uses to activate certificate changes.
Hub function and direct connect have no compile flags of their own: they are configured at runtime with
BACnetStack_SetBACnetSCHubFunctionConfig and BACnetStack_SetBACnetSCDirectConnectConfig (Appendix C.2), or,
after commissioning, by a WriteProperty to the two direct-connect enables (§16.5). BACnet/SC debug output
uses the stack-wide STACK_OPTION_API_DEBUG_LOGGING.
Appendix C - BACnet/SC Public API Cheat Sheet
Section titled “Appendix C - BACnet/SC Public API Cheat Sheet”All functions are declared in source/CASBACnetStackDLL.h and are exported in every build. Without
STACK_OPTION_DATA_LINK_LAYER_SC, each BACnet/SC function returns false (or does nothing, for the void
registration functions) and reports through the debug-message callback that the feature was not compiled in.
The three generic File-object entries in C.1 (BACnetStack_AddFileObject and the read and write file
callbacks) depend on STACK_OPTION_ATOMIC_FILE_IO with STACK_OPTION_OBJECT_TYPE_FILE instead; see B.1.
C.1 Configuration (call once at startup)
Section titled “C.1 Configuration (call once at startup)”bool BACnetStack_SetBACnetSCUuid( const uint8_t* uuid, uint8_t uuidLength); // 16
bool BACnetStack_SetBACnetSCHubConnector( const uint8_t* vmac, uint8_t vmacLength, // 6 const char* primaryHubUri, uint16_t primaryHubUriLength, const char* failoverHubUri, uint16_t failoverHubUriLength);
bool BACnetStack_SetBACnetSCSettings( uint16_t maximumBVLCLength, // 6144 default (TARGET_FULL), else 1600 uint16_t maximumNPDULength, // 1497 default uint16_t connectTimeoutSec, // 10 default uint16_t wsConnectTimeoutSec, // 10 default uint16_t wsReconnectTimeoutSec, // 10 default uint16_t heartbeatTimeoutSec); // 300 default
bool BACnetStack_SetBACnetSCHelloCapabilities(uint8_t cap);
bool BACnetStack_SetBACnetSCCertificateFileObjects( uint32_t deviceInstance, uint32_t scPortInstance, bool hasOperationalCertificateFile, uint32_t operationalCertificateFileInstance, bool hasCertificateSigningRequestFile, uint32_t certificateSigningRequestFileInstance, const uint32_t* issuerCertificateFileInstances, uint16_t issuerCertificateFileCount); // normally 2
// Creates a File object AND stores File_Access_Method / File_Read_Only - use this instead of// AddObject(..., OBJECT_TYPE_FILE, ...) for every certificate file (see section 16.1 of the BACnet/SC certificates page).bool BACnetStack_AddFileObject( uint32_t deviceInstance, uint32_t objectInstance, bool isWritable, bool isConfigurationFile, // = false uint8_t fileAccessMethod); // = 1 (streamAccess); property enum, record=0/stream=1
// Register once; the stack calls it on demand instead of holding a copy of the bytes (section 16.2 of the BACnet/SC certificates page).void BACnetStack_RegisterCallbackReadFile(bool(*fp)( uint32_t deviceInstance, uint32_t fileInstance, uint32_t fileStart, uint32_t requestedCount, uint8_t* fileData, uint32_t* fileDataLength, uint32_t maxFileDataLength, bool* endOfFile, uint32_t* errorCode));
void BACnetStack_RegisterCallbackWriteFile(bool(*fp)( uint32_t deviceInstance, uint32_t fileInstance, int32_t fileStart, const uint8_t* fileData, uint32_t fileDataLength, int32_t* ackFileStart, uint32_t* errorCode));C.2 Optional hub function / direct connect (server roles)
Section titled “C.2 Optional hub function / direct connect (server roles)”bool BACnetStack_SetBACnetSCHubFunctionConfig( uint32_t deviceInstance, uint32_t scPortInstance, bool enabled, uint16_t maxConnections);
// Startup configuration only - sets the current AND active enable state immediately, with no// Changes_Pending step. For FIELD reconfiguration after commissioning, use WriteProperty on// SC_Direct_Connect_Initiate_Enable / SC_Direct_Connect_Accept_Enable instead (see section 16.5 of the BACnet/SC certificates page).bool BACnetStack_SetBACnetSCDirectConnectConfig( uint32_t deviceInstance, uint32_t scPortInstance, bool initiateEnabled, bool acceptEnabled);
// Accept URIs are a list per role (SC_Hub_Function_Accept_URIs / SC_Direct_Connect_Accept_URIs), set separately.// connectionRole: 0 = hub function, 1 = direct connect.bool BACnetStack_AddBACnetSCAcceptUri( uint32_t deviceInstance, uint32_t scPortInstance, uint8_t connectionRole, const char* acceptUri, uint32_t acceptUriLength);bool BACnetStack_ClearBACnetSCAcceptUris( uint32_t deviceInstance, uint32_t scPortInstance, uint8_t connectionRole);Hub function forwarding has no rate limit. Cl. AB.5.1 has the hub function forward unicast messages and copy every broadcast to the other hub connections. The stack does this unconditionally: it applies no per-peer rate or volume limit, and BACnet/SC has no result code for “rate limited”. One connected peer’s broadcast therefore goes out (N−1) times across N hub connections, so size the hub host’s bandwidth and CPU for the broadcast load of the whole network. Flood control is the deployment’s responsibility: issue operational certificates only to trusted devices, revoke or remove the certificate of a device that misbehaves, and monitor hub traffic.
C.3 Notifications back into the stack
Section titled “C.3 Notifications back into the stack”bool BACnetStack_SetBACnetSCWebSocketStatus( const char* uri, uint16_t uriLength, uint8_t status, // 1=Connecting, 2=Connected, 3=Disconnected, 4=Error (see section 19 of the BACnet/SC transport callbacks page) uint32_t errorCode); // RFC 6455 close code with Error; otherwise 0C.4 Callbacks the application must implement
Section titled “C.4 Callbacks the application must implement”// Required for: Node (outbound connect to Hub or Direct Connection peer).// Optional for: Hub that also connects to another Hub as a client.void BACnetStack_RegisterCallbackInitiateWebsocket( bool(*fp)(const char* uri, uint32_t uriLength));
// Required for: every role. Closes outbound connections, and the accepted ones// (<acceptUri>|client=<token>) that the hub function and direct-connect accept close.void BACnetStack_RegisterCallbackDisconnectWebsocket( void(*fp)(const char* uri, uint32_t uriLength));
// Required for: Hub (accept inbound Node connections).// Required for: Node with Direct Connect Accept enabled.// Not required for: Node-only devices that do not accept Direct Connections.void BACnetStack_RegisterCallbackSCStartListening( bool(*fp)(const char* uri, uint32_t uriLength));
// Required for: Hub, and any Node with Direct Connect Accept enabled.// Not required for: Node-only devices that do not accept Direct Connections.void BACnetStack_RegisterCallbackSCStopListening( void(*fp)(const char* uri, uint32_t uriLength));
// For debug logging and observability of state-machine transitions.// Not required for correct operation.void BACnetStack_RegisterCallbackBACnetSCStateChange( void(*fp)(uint32_t deviceInstance, uint32_t networkPortInstance, uint8_t stateMachine, uint8_t previousState, uint8_t newState, const char* uri, uint32_t uriLength));C.5 Setup order (required)
Section titled “C.5 Setup order (required)”BACnetStack_RegisterCallback* // standard + SC callbacks (integration steps 3 and 4)BACnetStack_AddDeviceBACnetStack_AddObject ... // application objectsBACnetStack_AddFileObject x 4 // op cert + 2 issuer certs + CSR (section 16.1 of the BACnet/SC certificates page)BACnetStack_AddNetworkPortObject(SC, ..., networkNumber, networkNumberQuality, ...)BACnetStack_RegisterCallbackReadFile(...) // serves the PEM bytes your app holds (section 16.2 of the BACnet/SC certificates page)BACnetStack_RegisterCallbackWriteFile(...) // accepts/rejects writes into cert filesBACnetStack_SetBACnetSCUuidBACnetStack_SetBACnetSCHubConnectorBACnetStack_SetBACnetSCSettings // optionalBACnetStack_SetBACnetSCCertificateFileObjects // leaves the port's Changes_Pending TRUEBACnetStack_ActivateNetworkPortPendingChanges // activates the start-up configuration (section 16.3 of the BACnet/SC certificates page)loop: BACnetStack_TickAppendix D - Conformance and Interoperability Statement
Section titled “Appendix D - Conformance and Interoperability Statement”Standard: ANSI/ASHRAE Standard 135-2024 (BACnet), Protocol Revision 30 SC Specification: Annex AB of ANSI/ASHRAE 135-2024
D.1 Standards Conformance
Section titled “D.1 Standards Conformance”The CAS BACnet Stack BACnet/SC implementation targets conformance with:
- ANSI/ASHRAE 135-2024, Protocol Revision 30 — BACnet/SC transport specification
- ASHRAE 135.1-2023 — BACnet Conformance Test Plan (Clause 12.5: BACnet/SC)
- RFC 6455 — The WebSocket Protocol
- RFC 8446 — TLS 1.3 (BACnet/SC requires TLS 1.3; TLS 1.2 is not supported)
D.2 Implemented Features
Section titled “D.2 Implemented Features”| Feature | Status | Notes |
|---|---|---|
| Hub connector (node) role | Complete | Connects to primary and failover hubs |
| Hub function (hub) role | Complete | Accepts inbound node connections |
| Direct connect (initiate) | Complete | Peer-to-peer BACnet/SC |
| Direct connect (accept) | Complete | Accepts inbound peer connections |
| TLS mutual authentication | Host application | Done by the application’s TLS library; the stack has no TLS code |
| X.509 certificate validation | Host application | Performed by the application’s TLS library during the handshake; the stack performs no certificate checks |
| BVLC/SC framing | Complete | All BVLC message types implemented |
| Heartbeat | Complete | Configurable interval |
| Reconnect with backoff | Complete | Configurable min/max backoff |
| Network Port object (SC) | Complete | Hub connector state, UUID, etc. |
| Certificate File Objects | Complete | Application-owned bytes (§2.7) |
| Remote certificate update | Complete | AtomicWriteFile/WriteProperty + ReinitializeDevice |
| On-device CSR generation | Host application | The generate-csr-file network port command is forwarded to BACnetStack_RegisterCallbackNetworkPortCommand (§16.6), where the application generates the CSR and writes it into the CSR File object; with no callback it answers optional-functionality-not-supported |
| UUID management | Complete | Property 507 (Device_UUID) |
| State-change notification callback | Complete | All connection state transitions |