Skip to main content

BACnet/IP Driver

Overview

BACnet/IP — the standard protocol for building automation (HVAC / lighting / fire / access control). It operates over UDP/47808 with the BVLC + NPDU + APDU layers. Rewritten natively in 2026-07 — the old delegation to Apache PLC4j bacnet-ip has been removed (PLC4j 0.13.1 BACnet is canRead()=false (passive/EDE only), so it threw a "doesn't support reading" exception on connect, making collection impossible). It now uses the in-house BACnet/IP confirmed ReadProperty polling implementation in plantpulse-plc-protocol (BacnetIpPLC) — the same native pattern as AB-ETH.

ItemValue
opc_typeBACNET
Implementation classplantpulse.driver.protocol.bacnet.BACnetDriver
LibraryIn-house native (bacnet in plantpulse-plc-protocol — ReadProperty)
InheritanceBaseProtocolDriver (not PLC4j)
Default port47808 (0xBAC0, UDP)
StatusStable (ReadProperty polling)
readOK (ReadProperty, single property)
write❌ (read-only — WriteProperty not implemented)
SecurityNone (BACnet/SC not supported)
No EDE file required

The old PLC4j implementation statically mapped the object list from an EDE (Engineering Data Exchange) CSV file. The native driver does not use EDE files — it sends confirmed ReadProperty requests directly using the tag address. The ede-file-path / ede-directory-path options from the old form are ignored (removed).


Class Structure

BaseProtocolDriver
└── BACnetDriver — connect() 에서 BacnetIpPLC(host, port) 생성 (UDP connectionless)

UDP is connectionless, so there is no handshake — having the socket ready is the connection, and communication liveness is managed by setConnected(false) on read failure (the AB-ETH pattern). On a read failure the socket is invalidated so the Edge is prompted to reconnect.


Wire Format Summary (In-house Implementation)

  • BVLC (Block Virtual Link Control) → NPDU (Network) → APDU (Application).
  • ReadProperty-Request: objectIdentifier + propertyIdentifier.
  • ReadProperty-ACK: decodes BACnet primitive types (Real / Unsigned / Boolean / Enumerated / CharacterString) into strings.
  • BBMD (Foreign Device Registration) is not used — operates only within the broadcast domain of the same subnet.

OPC Registration Options

FieldMeaningDefault
opc_agent_ipBACnet device IP192.168.1.50
opc_agent_portUDP port47808 (normally fixed)
options.read-timeoutResponse timeout (ms)3000

Tag plc_address Format

<object-type>:<instance>[:<property>]

If property is omitted, present-value is assumed. Both object-type and property accept either a name or a numeric code. The old PLC4j notation (a dot before the property: analog-input:1.present-value) is also accepted for compatibility.

NotationMeaning
analog-input:0present-value of AI 0 (REAL) — property omitted
analog-input:0:present-valueSame (explicit)
binary-input:1BI 1 present-value (Boolean)
analog-value:10AV 10 (calculated value)
multi-state-input:2MSI 2 (Unsigned)
device:1:object-nameName of Device 1 (CharacterString)
0:5:85object-type 0 (AI) / instance 5 / property 85 (present-value) — numeric codes

Supported / Unsupported

  • read: ReadProperty (single property) — OK.
  • write: not supportedisWriteSupported() = false and write() always return false. (WriteProperty is a follow-up item.)
  • COV (Change Of Value) subscription: not supported — polling based.
  • BBMD / Foreign Device: not supported — same subnet only.
  • BACnet/SC (Secure Connect WebSocket) and BACnet MS/TP serial: not supported.

curl Registration Example

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_BACNET_AHU1",
"opc_type": "BACNET",
"opc_name": "AHU-1 Controller",
"opc_agent_ip": "192.168.1.50",
"opc_agent_port": "47808",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 2000,
"options": { "read-timeout": "3000" },
"tag_list": [
{"tag_id":"OPC_BACNET_AHU1_T01","tag_name":"SupplyTemp","plc_address":"analog-input:0","data_type":"Float"},
{"tag_id":"OPC_BACNET_AHU1_T02","tag_name":"FanRun","plc_address":"binary-input:1","data_type":"Boolean"}
]
}'

References

  • BACnet ASHRAE 135 standard.
  • Code: plantpulse-edge-driver/src/plantpulse/driver/protocol/bacnet/BACnetDriver.java + the bacnet package in plantpulse-plc-protocol.
  • Operation: the same L2 broadcast domain is required. Devices beyond a router require a BBMD or a separate gateway (not supported).