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.
| Item | Value |
|---|---|
opc_type | BACNET |
| Implementation class | plantpulse.driver.protocol.bacnet.BACnetDriver |
| Library | In-house native (bacnet in plantpulse-plc-protocol — ReadProperty) |
| Inheritance | BaseProtocolDriver (not PLC4j) |
| Default port | 47808 (0xBAC0, UDP) |
| Status | Stable (ReadProperty polling) |
| read | OK (ReadProperty, single property) |
| write | ❌ (read-only — WriteProperty not implemented) |
| Security | None (BACnet/SC not supported) |
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
| Field | Meaning | Default |
|---|---|---|
opc_agent_ip | BACnet device IP | 192.168.1.50 |
opc_agent_port | UDP port | 47808 (normally fixed) |
options.read-timeout | Response 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.
| Notation | Meaning |
|---|---|
analog-input:0 | present-value of AI 0 (REAL) — property omitted |
analog-input:0:present-value | Same (explicit) |
binary-input:1 | BI 1 present-value (Boolean) |
analog-value:10 | AV 10 (calculated value) |
multi-state-input:2 | MSI 2 (Unsigned) |
device:1:object-name | Name of Device 1 (CharacterString) |
0:5:85 | object-type 0 (AI) / instance 5 / property 85 (present-value) — numeric codes |
Supported / Unsupported
- read: ReadProperty (single property) — OK.
- write: not supported —
isWriteSupported() = falseandwrite()always returnfalse. (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+ thebacnetpackage inplantpulse-plc-protocol. - Operation: the same L2 broadcast domain is required. Devices beyond a router require a BBMD or a separate gateway (not supported).