Modbus Gateway in BACRouter

From firmware version 4.x,  BACRouter has builtin Modbus gateway functionality. This article will try to explain the underlying mechanism.

Modbus Read

To improve performance, BACRouter will read as more as possible data in one transcation. In Modbus standard, at most 2000 bits (coil or discrete) or 125 registers (input or holding register) could be read in one request, but specific Modbus slave device may only support a lower value.  In BACRouter’s WebUI, there is a Modbus parameter edit dialog in “Slave Settings”, “Multiply read bits” and “Multiply read registers” parameters define the ability of the slave device.

BACRouter will not segment reading of single point even the “Multiply read registers” is less than registers used by the point.

Usually the data we care for has discontinuous Modbus address, for example, We only care for coils on address 1 and 2000, though we could read coil data from address 1 to 2000 in one request and skip returned data from address 2 to 1999,  sending a request to read coil on address 1, then sending another request to read address 2000 maybe more efficient. “Bits skipped” and “Registers skipped” parameter define how many unused data units between 2 cared data units are allowed to be skipped. As previous example, if “Bits skipped” is set to 1998, the BACRouter will try to read coil 1 and 2000 in single request. If “Bits skipped” is set to 1997, then BACRouter will read Coil 1 first, then Coil  2000, because bits skipped is 1998 and is greater than the setting of 1997.

When point’s data age which is defined as time past from data updating, is greater or equal to “Update interval”, BACRouter will queue read request try to update it.

Modbus Write

BACRouter will not combine multiple writing demand into one writing request.

For writing single data unit, there are two Modbus function codes available. For example, function code 5 is writing single coil,  function code 15 is writing multiple coils but the quantity of coil to write could be assigned to 1. Some devices only support one of them.  “Coil write singly” parameter will specify which function code is used.

In the same way, “Register write singly” is applied to register writing. If any BACnet output object maps to multiple holding registers, it implies the slave device support function code 16 for write multiple registers, function code 16 will be used for writing any register whatever “Register write singly” is set.

To avoid too frequently sending write request to slave device, BACRouter will wait “Write interval” from last write.


If any read/write request failed, BACRouter will retry it, when the continuous fail count reachs 3, BACRouter will regard the slave device as offline. In offline state, BACRouter will retry the request every “Offline update interval”.

The BACnet side reaction for offline event will be described later.

Modbus Test

“Batch read” test method could be used to verify slave’s support for “Multiply read bits”/”Multiply read registers” and validity of address space.

“Point read/write” test method will help user to confirm the data type and byte order of complex points.

To perform a test, a master should be defined and enabled, then be saved into BACRouter to take effect.

Modbus Serial Bus

BACRouter supports serial slave devices with different baudrate, parity, RTU/ASCII mode on same RS485 bus. Before sending request, BACRouter will change it serial port parameter based on slave device’s settings.

It should be safe to have 2 devices with same slave address but different baudrate or RTU/ASCII  mode, but preventing collision based on different parity might not be enough, because some devices do not examine parity error.

Single Device Mapping Mode

If VBUS network port is not enabled, BACRouter will work in single device mapping mode, every Modbus slave device will be mapped into exclusive object instance space of 1000 in BACnet device defined in “Application Settings”.  for example, first device will occupy 0~999, next device will occupy 1000~1999, so on.

If “Packet capture mapping” in “Application Settings” page is enabled, the packet capture mapping module will occupy first 1000 instance space, that is said 0~999.  Slave device will go after it.

BACRouter will create one StructuredView object for each slave device,  the object name is loaded from slave device’s “Device name”.

In this mode, each slave’s “Device name” and “Point name” shall be unique to global scope, including “Device name” defined in “System Settings” and object name used by packet capture mapping module. To avoid name collision, there is a “Prefix to name” in “Slave Settings”, this string will be prepended to slave’s “Device name” and “Point name”.

When the slave device turn to offline in this mode, all points’ reliability will be set to “COMMUNICATION_FAILURE”, if slave device is back to online, upon updating of point value, the point’s reliability will be set back to “NO_FAULT_DETECTED”.

Virtual Device Mapping Mode

If VBUS network port is enabled. Each slave device will be mapped to a virtual BACnet device on VBUS network. The MAC address will start from 1.

In this mode, each slave shall has a unique “Device instance”.

When the slave device turn to offline in this mode, the virtual BACnet device will stop sending/receiving any packet to simulate offline state. if slave device is back to online, after updating all point’s value, the virtual BACnet device will quite offline state.

Object Instance Assignment

Object Instance is automatically assigned by growth in order of define. The actual object id used by point could be read from “Mapping” dialog box on “Slave Settings” page.

Output Object

The point value of output object is still be polled and set to object’s Relinguish_Default property. If the value read back does not match last value written, BACRouter will set the reliability of that object to “UNRELIABLE_OTHER”,  and if the time past from last writing is longer than “Re-write interval”, BACRouter will queue another write.

Because of above verifying mechanism, if the Modbus addresses of 2 output objects overlap, weird behavior would be expected, the WebUI will prevent such overlap in slave’s configuration.

NaN For Float And Double

NaN is a special value defined by IEEE-754, BACRouter does not accept NaN (Infinite is still valid for BACRouter).

When a point is defined as Float and Double, but NaN is read from slave, BACRouter will set the reliability to “UNRELIABLE_OTHER”.