BACnet
BACnet is a protocol developed by ASHRAE for building automation and control networking, specifically designed to meet the communication needs of building automation and control systems for applications such as heating, ventilating, air-conditioning control, lighting control, access control and fire detection systems. HSYCO conforms to a functional subset of the BACnet protocol, as defined in ANSI/ASHRAE Standard 135. This I/O Server configures HSYCO as a BACnet client device, over BACnet/IP or BACnet/SC. To expose HSYCO’s own objects to other BACnet devices, use the BACnetServer I/O Server instead. BACnet support is implemented as an I/O Server with a data point representation that you can use in the EVENTS language and Java to read and write BACnet’s objects and properties, and to manage asynchronous notification services.
The BACnet Vendor Identification Number that has been issued by ASHRAE to HSYCO S.r.l. (formerly Home Systems Consulting SpA) is 590.
Communication
The I/O Server supports two datalinks, selected with the transport option:
- BACnet/IP (
transport=ip, the default): the BACnet messaging protocol is transported over standard IP networks, on the local network. Routing of BACnet traffic is not supported, so you should configure one I/O Server for each LAN used for BACnet traffic. Support for multiple BACnet/IP local networks is achieved by configuring one I/O Server for each network, while devices on remote routed subnets can be reached by registering with a BBMD, see Foreign Device Registration. Several BACnet I/O Servers can share the same UDP port: the first one to start owns the socket, and inbound packets are dispatched to the other I/O Servers based on the network each packet belongs to. - BACnet/SC (
transport=sc): BACnet Secure Connect, where BACnet messages travel over WebSocket connections secured with mutual TLS. HSYCO connects as an SC node to a hub, with optional failover hub, and can also establish direct node-to-node connections. See BACnet/SC below.
The BACnet I/O Server supports segmented messages in both directions: it reassembles segmented responses (Read-Property and Read-Property-Multiple), and it segments outgoing Write-Property requests that don’t fit a single APDU, provided the remote device declares that it can receive segmented messages.
HSYCO Configuration
Add a BACNET I/O Server in the I/O Servers section of the Settings and set its parameters:
Communication
- IP Address: broadcast IP address of the LAN used for BACnet traffic (e.g. 192.168.1.255). Required when
transport=ip, where it also selects the local network interface to bind. Not used whentransport=sc, where the hub and peer addresses come from the URI options, and can be left blank. - IP Port: UDP port to use, leave blank to use default port 47808. Only applies when
transport=ip.
High Availability
- Shutdown when inactive: defaults to false.
Options
| ID | Default | Values | Description |
|---|---|---|---|
| deviceid | 590001 | 0 ... 4194302 | BACnet device identifier assigned to the HSYCO BACnet client |
| transport | ip | ip | BACnet/IP datalink, over UDP |
| sc | BACnet/SC datalink, over WebSocket and TLS | ||
| disableverbosedump | false | true | when verbose log is enabled, don’t write IP packets dump to the log files |
| false | when verbose log is enabled, all BACnet IP packets are written to the log files | ||
| eventslog | false | true | if the general eventsLog option is also true in System Settings, BACnet events for this gateway are written in the log files |
| false | BACnet events for this gateway are not written in the log files, even if the eventsLog option is true in System Settings |
Segmentation Options
These options tune the segmentation engine, and apply to both datalinks.
| ID | Default | Values | Description |
|---|---|---|---|
| apdusegmenttimeout | 2000 | milliseconds | time to wait for the acknowledgement of a segment before retrying |
| apduretries | 3 | 0 ... N | number of retries of an unacknowledged segment |
| segmentwindow | 16 | 1 ... 127 | proposed window size, i.e. the number of segments sent before waiting for an acknowledgement |
| maxapdu | 1476 | 50, 128, 206, 480, 1024, 1476 | maximum APDU length accepted, advertised in the I-Am and in every confirmed request. It limits the size of the responses and notifications the devices send to HSYCO, not the size of the requests HSYCO sends them. Ignored when transport=sc, where the negotiated maxnpdu is used instead |
HSYCO accepts messages of up to 64 segments, the maximum number that can be advertised as an exact value. A device that exceeds either that count or maxapdu × 64 total octets is aborted with the buffer-overflow reason, so lowering maxapdu also lowers the size of the largest message that can be reassembled.
Foreign Device Registration Options
These options only apply when transport=ip. Setting bbmd makes HSYCO register as a foreign device (ANSI/ASHRAE 135, Annex J) with a BACnet Broadcast Management Device, to communicate with BACnet/IP devices on remote routed subnets that local broadcasts cannot reach.
While registered, broadcast requests like Who-Is are sent both on the local network and to the BBMD, which distributes them to its own network and to the other registered foreign devices; broadcasts originated on the remote networks are received through the BBMD, so remote devices are discovered exactly like local ones. Responses and confirmed requests always travel directly between HSYCO and the remote devices, without passing through the BBMD.
The registration is renewed automatically at half the configured time-to-live, and re-established with periodic retries when the BBMD becomes unreachable. Registration failures are reported in the log, at most one line per minute; communication on the local network is never affected by the state of the registration.
HSYCO itself is not a BBMD: BBMD-role requests received from other devices — foreign device registrations, broadcast distributions, table reads and writes — are refused with the corresponding NAK code.
| ID | Default | Values | Description |
|---|---|---|---|
| bbmd | host or host:port | address of the BBMD to register with, e.g. 192.168.10.1 or 192.168.10.1:47808; the default port is 47808. When not set, foreign device registration is disabled | |
| bbmdttl | 600 | 1 ... 65535 | registration time-to-live in seconds, renewed by HSYCO at every half bbmdttl. Lower values make a stale registration expire sooner on the BBMD, at the cost of more frequent renewals |
BACnet/SC Options
These options only apply when transport=sc.
| ID | Default | Values | Description |
|---|---|---|---|
| huburi | URI | URI of the primary hub, e.g. wss://hub.example.com:47800 | |
| failoveruri | URI | URI of the failover hub. HSYCO connects to it when the primary hub is unreachable, and periodically probes the primary to fail back | |
| keystore | secrets/hsyco.keys | file name | key store holding the operational certificate and private key of this node |
| keystorepass | <hsyco key store password> | password | password of the key store |
| truststore | secrets/hsyco.keys | file name | trust store holding the issuer certificate used to validate the peers’ certificates |
| truststorepass | <hsyco client keystore password> | password | password of the trust store |
| certfile | file name | operational certificate, in PEM format. When both certfile and keyfile are set, the PEM credentials are used and the key store options are ignored | |
| keyfile | file name | private key of the operational certificate, in PEM format | |
| cafile | value of certfile | file name | issuer certificate used to validate the peers’ certificates, in PEM format |
| vmac | random | 6 bytes, hex | VMAC address of this node, e.g. 0a1b2c3d4e5f. When not set, a random VMAC is generated at first startup and persisted |
| uuid | random | UUID | device UUID of this node. When not set, a random UUID is generated at first startup and persisted |
| acceptdirect | false | true | accept incoming direct connections from other nodes |
| false | only communicate through the hub | ||
| directlisturi | URI | URI to accept direct connections on, advertised to the other nodes, e.g. wss://192.168.1.10:47800 | |
| directpeers | list | static list of peers to connect directly to, as a space or comma separated list of <vmac>@<uri> entries. Multiple URIs for the same peer are separated by | | |
| maxbvlc | 1497 | 1497 ... 61327 | maximum BVLC message size advertised to the peers |
| maxnpdu | 1497 | 1497 ... 61327 | maximum NPDU size advertised to the peers. It also replaces maxapdu as the maximum APDU length accepted |
The VMAC and device UUID of the node are persisted in the identity.ini file of the I/O Server, in the state/drivers/bacnet/<ioserver_name> directory, where <ioserver_name> is the I/O Server name in lower case, so that the node keeps the same identity across restarts. An explicitly configured vmac is never changed by HSYCO, while a generated one can be re-rolled in case of a collision with another node.
BACnet Utility
The BACnet Utility provides a graphical interface to browse all BACnet devices available on the network, to show all objects of a device, and all properties and values of any object instance, and to configure the periodic polling described below.
Polling
Devices that don’t support change of value notification have no way to keep HSYCO’s datapoints up to date on their own. The Polling engine reads them periodically instead, with ReadProperty and ReadPropertyMultiple requests, and without writing any EVENTS logic: polled values land on the normal datapoints, so any EVENTS trigger bound to them fires exactly as it does for a manual read.
Polls are configured one row per object or property, from the Polling page of the BACnet Utility, and are persisted in the poll.txt file of the I/O Server, in the config/drivers/bacnet/<ioserver_name> directory. See Polling for the page, the file format and the per-row statistics.
Rows targeting the same device are polled on a shared schedule: single-property rows that fall due on the same cycle are combined into a single ReadPropertyMultiple request for that device, up to 16 object-property references per request (further requests are used beyond that limit); a single due row for a device is sent as a plain ReadProperty. Rows that poll all properties of an object are always dispatched individually, one ReadPropertyMultiple per object, and are never combined with other rows.
The EVENTS readproperty: command together with a PROGRAMTIMER, shown in the Examples below, remains available for logic-driven polling that needs conditions or sequencing beyond what a polling row expresses.
Datapoints
The following services are supported:
- Alarm and Event Services:
- ConfirmedCOVNotification
- UnconfirmedCOVNotification
- SubscribeCOV
- SubscribeCOVProperty
- Object Access Services:
- ReadProperty
- ReadPropertyMultiple
- WriteProperty
- Remote Device Management Services:
- Who-Is
- I-Am
HSYCO issues Who-Is requests to discover the devices it needs to communicate with, and also answers the Who-Is requests it receives with an I-Am response.
A request addressed to a device whose address isn’t known yet, or whose cached address is older than 60 seconds, doesn’t block: it is queued while a Who-Is is sent, and is transmitted as soon as the device answers with its I-Am. The Who-Is is repeated, and requests still queued after 5 seconds are discarded, with one line in the log naming the device that didn’t answer.
The BACnet I/O Server supports all standard object types and properties for the ReadProperty service, while object’s properties that could be written using the WriteProperty service are listed in the supported object types table.
Following HSYCO’s naming convention for BACnet, all object types and properties are written in lowercase letters with no spaces or underscore characters. For example, Present_Value is written as presentvalue, and the binary input object type is written as binaryinput.
The BACnet datapoint names always begin with the same format:
<device_id>.<object_type>.<object_id>
where <device_id> is the BACnet device numeric identifier, <object_type> is the object type name with no spaces and all lowercase and <object_id> is the object instance numeric identifier.
Data point names returning properties values also have the object property identifier at the end with no spaces and all lowercase characters:
<device_id>.<object_type>.<object_id>.<property_id>
For example this datapoint
123.analogoutput.1.presentvalue
represents the present value of the analog output BACnet object "1" of the BACnet device "123".
Datapoints Table
| ID | Value | R/W | Description |
|---|---|---|---|
| <device_id>.<object_type>.<object_id> | readproperty:<property_id> | W | executes the ReadProperty Service request, reading the <property_id> property of the <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id>.<property_id> | <value> | R | the <property_id> property value> read from <device_id>.<object_type>.<object_id> object, formatted according to the application datatype |
| <device_id>.<object_type>.<object_id>[<N>] | readproperty:<property_id> | W | executes the ReadProperty Service request, reading the <property_id> property of the <device_id>.<object_type>.<object_id> object at index [<N>] |
| <device_id>.<object_type>.<object_id>.<property_id>[<N>] | <value> | R | executs the <property-id> property value> read from <device-id>.<object-type>.<object-id> object, formatted according to the application datatype. (if this property is an array of values) |
| <device_id>.<object_type>.<object_id> | writeproperty:<property_id>:<value> | W | executes the WriteProperty Service request, writing value> in the <property_id> property of <device_id>.<object_type>.<object_id> object , without specifying the priority |
| <device_id>.<object_type>.<object_id> | writeproperty/<P>:<property_id>:<value> | W | executes the WriteProperty Service request, writing value> in the <property_id> property of <device_id>.<object_type>.<object_id> object , specifying the priority <P>, as a number from 1 to 16 |
| <device_id>.<object_type>.<object_id> | writeproperty/<P>:<property_id> | W | cancel the last value set at priority <P> in the <property_id> property of <device_id>.<object_type>.<object_id> |
| <device_id>.<object_type>.<object_id>[<N>] | writeproperty:<property_id>:<value> | W | executes the WriteProperty Service request, writing <value> in the <property_id> property array index <N> of <device_id>.<object_type>.<object_id> object , without specifying the priority |
| <device_id>.<object_type>.<object_id>[<N>] | writeproperty/<P>:<property_id>:<value> | W | executes the WriteProperty Service request, writing <value> in the <property_id> property array index <N> of <device_id>.<object_type>.<object_id> object , specifying the priority <P>, as a number from 1 to 16 |
| <device_id>.<object_type>.<object_id>[<N>] | writeproperty/<P>:<property_id> | W | cancel the last value set at priority <P> in the <property_id> property of <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecov | W | sends a SubscribeCOV request with an indefinite lifetime for <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecov:confirmed | W | sends a SubscribeCOV request with an indefinite lifetime and the confirmed notifications option enabled for <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecov:unconfirmed | W | sends a SubscribeCOV request with an indefinite lifetime and the confirmed notifications option disabled for <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecov:cancel | W | sends a SubscribeCOV cancellation request for <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecovproperty:<property_id> | W | sends a SubscribeCOVProperty request with an indefinite lifetime and the confirmed notifications option enabled, for the <property_id> property of the <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecovproperty:<property_id>:confirmed | W | same as above, with the confirmed notifications option explicitly enabled |
| <device_id>.<object_type>.<object_id> | subscribecovproperty:<property_id>:unconfirmed | W | sends a SubscribeCOVProperty request with an indefinite lifetime and the confirmed notifications option disabled, for the <property_id> property of the <device_id>.<object_type>.<object_id> object |
| <device_id>.<object_type>.<object_id> | subscribecovproperty:<property_id>:cancel | W | sends a SubscribeCOVProperty cancellation request for the <property_id> property of the <device_id>.<object_type>.<object_id> object |
Supported Object Types
The I/O Server recognizes all the object types standardized up to ANSI/ASHRAE 135-2016, and can read any of their properties by identifier. The table below lists the properties that can also be written with the WriteProperty service.
| Object Type | Writable Properties |
|---|---|
| accumulator | * devicetype * presentvalue * pulserate * valuebeforechange * valueset * units * maxpresvalue * eventstate * outofservice * statusflags |
| analoginput | * devicetype * covincrement * presentvalue * units * eventstate * outofservice * statusflags |
| analogoutput | * devicetype * covincrement * presentvalue * relinquishdefault * units * eventstate * outofservice * statusflags |
| analogvalue | * covincrement * presentvalue * relinquishdefault * units * eventstate * outofservice * statusflags |
| averaging | * attemptedsamples * windowinterval * windowsamples |
| binaryinput | * activetext * devicetype * inactivetext * presentvalue * polarity * eventstate * outofservice * statusflags |
| binaryoutput | * activetext * devicetype * inactivetext * presentvalue * relinquishdefault * polarity * eventstate * outofservice * statusflags |
| binaryvalue | * activetext * inactivetext * presentvalue * relinquishdefault * eventstate * outofservice * statusflags |
| command | * presentvalue |
| device | * systemstatus * modelname * databaserevision |
| lifesafetypoint | * devicetype * mode * reliability * trackingvalue |
| lifesafetyzone | * devicetype * mode * reliability * trackingvalue |
| loop | * covincrement |
| multistateinput | * devicetype * presentvalue * numberofstates * eventstate * outofservice * statusflags |
| multistateoutput | * devicetype * presentvalue * relinquishdefault * numberofstates * eventstate * outofservice * statusflags |
| multistatevalue | * presentvalue * relinquishdefault * numberofstates * eventstate * outofservice * statusflags |
| program | * programchange |
| pulseconverter | * adjustvalue * covincrement * presentvalue |
| accessdoor | * dooralarmstate * doorstatus * lockstatus * presentvalue * relinquishdefault |
| loadcontrol | * enable * requestedshedlevel (as Real number) |
The description and objectname properties can be written on any object type.
Supported Application Datatypes
| Tag# | Description | HSYCO Data Type | ||||
|---|---|---|---|---|---|---|
| 0 | Null | null | ||||
| 1 | Boolean | 0: false 1: true | ||||
| 2 | Unsigned Integer | 0 ... (232 - 1) | ||||
| 3 | Signed Integer | -231 ... (231 - 1) | ||||
| 4 | Real (IEEE-754 floating point) | floating point number (with decimal point separator) | ||||
| 5 | Double (IEEE-754 double precision floating point) | double precision floating point number (with decimal point separator) | ||||
| 6 | Octet String | the hexadecimal representation of byte values, for example: 3b0ac2ff | ||||
| 7 | Character String | a string. The default character set is UTF-8. Additional supported character sets are: ISO-8859-1, UCS-2, EUC-JP | ||||
| 8 | Bit String | a string representing a sequence of bit values, for example: 111010001 | ||||
| 9 | Enumerated | 0 ... (232 - 1) | ||||
| 10 | Date | <yyyy>/<mm>/<dd>[/<dow>] | <yyyy> is the year number; a * character is a wildcard matching any year, or an unspecified value. | <mm> is the month number, with January as 1; a * character is a wildcard matching any month, or an unspecified value; odd represent all odd months; even represent all even months. | <dd> is the day number, starting from 1 for the first day of the month; a * character is a wildcard matching any day, or an unspecified value; odd represent all odd days; even represent all even days; last represent the last day of the month. | <dow> is the week day, and is only present if set to a day of the week, and not to the wildcard/unspecified value; days are represented as mon, tue, wed, thu, fri, sat, sun |
| 11 | Time | <hh>:<mm>:<ss>.<ff> | <hh> is the hour, using a 24-hour clock from 0 to 23; a * character is a wildcard matching any hour, or an unspecified value. | <mm> is the minute, from 0 through 59; a * character is a wildcard matching any minute, or an unspecified value. | <ss> is the seconds, from 0 through 59; a * character is a wildcard matching any second, or an unspecified value. | <ff> is the hundreds of seconds number, from 0 to 99; a * character is a wildcard matching any fraction of second, or an unspecified value. |
| 12 | BACnetObjectIdentifier | <object_type>.<object_id> | <object_type> is represented with the type name for supported types, and with the BACnet object type number for unsupported objects. | <object_id> is the object instance number |
Properties whose value is a BACnetDateTime, such as the Modification_Date of a File object, are returned as a single <date> <time> string, combining the Date and Time datatypes above.
Examples
Reading the present value of an object
TIME : IO bac.123.analogoutput.1 = readproperty:presentvalue
IO bac.123.analogoutput.1.presentvalue : uiset mydata.text = IO bac.123.analogoutput.1.presentvalue
The first line executes the ReadProperty service to poll device 123 for the value of the Present Value property of object type Analog Input, instance 1. The second line sets a text object of the Web interface with the value read from the BACnet device.
Reading several values at once
HSYCOSTART : PROGRAMTIMER readbacnet = repeat 10
PROGRAMTIMER readbacnet : IO bac.123.analogoutput.1 bac.123.analogoutput.2 = readproperty:presentvalue
IO bac.123.analogoutput.1.presentvalue : uiset mydata1.text = IO bac.123.analogoutput.1.presentvalue
IO bac.123.analogoutput.2.presentvalue : uiset mydata2.text = IO bac.123.analogoutput.2.presentvalue
The second line is a compact form for issuing the ReadProperty service for multiple objects. The first line shows how to create a programtimer instance that is executed every 10 seconds.
Writing the present value of an object
USER bac = 5 : IO bac.21.pulseconverter.0 = writeproperty:adjustvalue:-3
TIME : IO bac.21.analogvalue.0 = writeproperty/14:presentvalue: $myvalue
The first line sets the Adjust Value property of object type Pulse Converter, object instance 0 in device 21 to the value -3.
The second line is similar, but this time the priority is set to 14. Also note that the value after presentvalue: is from a variable. To ensure that the variable name is translated to its value, leave a space before the variable name. This way the EVENTS interpreter will automatically concatenate the strings, also replacing the variable name with its value.
Subscribe for change of value notifications
TIME : IO bac.21.analoginput.0 bac.21.binaryinput.2 = subscribecov
TIME : IO bac.21.binaryvalue.0 = subscribecov:unconfirmed
The first line sends a SubscribeCOV request, with confirmed notifications, for two distinct objects. The second line also sends a SubscribeCOV request, but with unconfirmed notifications. This is a less reliable notification mechanism, but more efficient in terms of network usage and devices workload.
Subscribe for change of value notifications of a single property
TIME : IO bac.21.analoginput.0 = subscribecovproperty:presentvalue
TIME : IO bac.21.analoginput.0 = subscribecovproperty:statusflags:unconfirmed
The SubscribeCOVProperty service subscribes to a specific property of an object, instead of the object's standard set of monitored properties.
Cancel change of value notifications
TIME : IO bac.21.analoginput.0 bac.21.binaryinput.2 = subscribecov:cancel
TIME : IO bac.21.analoginput.0 = subscribecovproperty:presentvalue:cancel
These commands cancel the previous change of value notification requests.
Connecting to a BACnet/SC hub
bacnet.transport = sc
bacnet.huburi = wss://hub.example.com:47800
bacnet.failoveruri = wss://hub2.example.com:47800
bacnet.certfile = certs/node1.crt.pem
bacnet.keyfile = certs/node1.key.pem
bacnet.cafile = certs/ca.crt.pem
These options, set in the I/O Server’s configuration, connect HSYCO as a BACnet/SC node to a primary hub, with a failover hub, using PEM certificates. Once connected, all datapoints work exactly as they do over BACnet/IP.
Release Notes
4.2.0
- added support for the BACnet/SC (Secure Connect) datalink, with hub and failover hub connections, direct node-to-node connections, and TLS credentials from either a key store or PEM files
- added foreign device registration with a BBMD, to communicate with BACnet/IP devices on remote routed subnets
- added support for segmented messages on all confirmed services, both received and transmitted
- added support for the SubscribeCOVProperty service
- added support for several BACnet I/O Servers with the same UDP port number on different networks
- extended the set of writable properties, and added the device object
- added the polling engine and the Polling page of the BACnet Utility, with per-row statistics, per-device request batching, variable-gated rows, enabled/disabled rows, guided device/object/property selection, and the Poll shortcut buttons of the objects and properties tables
4.0.1
- added support for Read-Property-Multiple-Service
3.8.0
- improved network compatibility when using multiple network interfaces
3.4.0
- added support for segmented messages, limited to Read-Property-Response
3.3.0
- added support for the Relinquish_Default property
- the BACnet Utility is now fully integrated in the Manager tools
3.1.0
- initial release
BACnet is a registered trademark of the American Society of Heating, Refrigerating and Air-Conditioning Engineers, Inc.