The rexhip Modbus library–part of the Siemens Open Library–is an open-source extension to the base Modbus functions included in TIA Portal. It provides user-friendly Modbus master function blocks (FBs) for both TCP and RTU applications, as well as a host of pre-configured device profiles called “station blocks” (SBs), which include all the necessary queries to communicate with various common hardware. This guide will cover how to set up Modbus communications in TIA Portal using this library and the tools it provides for troubleshooting.
This guide focuses on using the rexhip library, not on general Modbus theory. Topics such as Modicon addressing details, data length inference, byte/word ordering, concurrency limits, and protocol-level RTU vs TCP differences are intentionally omitted where they are handled internally by the library or are covered in standard Modbus documentation.
The “mb_query” Function Block
Concept
The mb_query FB defines a Modbus request that gets sent by a Modbus master FB. Each defined query gets passed into the master block, where it is automatically handled one at a time. Queries can be defined independent of Station Blocks (SBs), but are often defined within SBs because of their improved diagnostic and troubleshooting features. We will cover this more in-depth later.
mb_query FB Input Parameters
The following parameters are passed into mb_query to define a query:
mb_addr: Modbus device address (slave address/unit identifier)
mode: This is the Modbus function code. The following are pre-defined tags in mb_query for the supported function codes:
Table 1: mb_query Modes
| Struct | Tag(Mode) | Type | Modbus Function Code |
| Read | “discrete_output” | Coil | 01 |
| Read | “discrete_input” | Discrete Input | 02 |
| Read | “holding_reg” | Register | 03 |
| Read | “input_reg” | Register | 04 |
| Write | “single_discrete_output” | Coil | 05 |
| Write | “single_holding_reg” | Register | 06 |
| Write | “discrete_output” | Coil | 15 |
| Write | “holding_reg” | Register | 16 |
data_addr: This is the starting address of the desired data within the Modbus data area. The rexhip library uses zero-based addressing.
Note: Some Modbus devices display registers as one-based, so if values are off by one register, this is probably why.
data_addr does not refer to values such as 40001, which are often found on datasheets.
Table 2: data_addr Examples
| Datasheet Notation | Operation and Data Type | Function Code (mode) | data_addr |
| 00001 | Read Coil | 01 | 0 |
| 00025 | Write Multiple Coils | 15 | 24 |
| 30020 | Read Input Register | 04 | 19 |
| 40005 | Write Single Holding Register | 06 | 4 |
| 40256 | Write Multiple Holding Registers | 16 | 255 |
dara_ptr: This is an Input/Output where the data that will be sent or received is stored. It can be any data type, including arrays and UDTs.
data_len: This is the length of the data in the query – the number of either bits or 16-bit registers for coils/holding registers, respectively, that will be read/written, starting at data_addr.
This is an optional, generally discouraged parameter to specify; it will be automatically calculated from the length of data_ptr by default.
If you want to override the automatic calculation, assign data_len to the desired length, and then set it back to automatic after the mb_query function call using #mb_query.data_len := #mb_query.c.auto_len
Modicon Addressing Convention
The rexhip library supports the Modicon convention for data addressing with the following static tags in mb_query to be used when declaring the mode of the query:
Table 3: Modicon Convention Modes
| Struct | Tag (Mode) | Modbus Function Codes |
| Mode | “read” | 01, 02, 03, 04 |
| Mode | “write_single” | 05, 06 |
| Mode | “write” | 15, 16 |
When using one of the above modes, data_addr declarations take the form found on datasheets (e.g., 40005).
Examples of mb_query Calls
The following is an example of mb_query function calls modified from one of the station blocks included in the rexhip library. In this scenario, three queries are being configured, two of which read input registers and one of which reads holding registers.

The following example shows an mb_query function call used to configure a request that writes multiple values to Modbus holding registers. The values are consolidated into an array, which is then passed to mb_query as a single data_ptr. The first array value will be written to the register at data_addr, the second value will be written to the register at data_addr + 1, and so on.

The “mb_query_bits” Function Block
The mb_query FB is designed to process data in bytes, so any queried booleans must total to whole bytes. If the number of queried bits does not equal complete bytes, either query a Boolean array with a length divisible by 8 (leaving unused bits as needed) or use the mb_query_bits FB instead.

Station Blocks
Concept
SBs group queries together, so the master block treats them as belonging to the same Modbus device. Device-level execution context is helpful for both tracking device communication and handling query timeouts.
If a device encounters communication errors, queries inside the SB can be skipped together with occasional retries, while other devices keep running.
This prevents one problematic device from interfering with the entire bus. Also, it is easy to set up multiple devices of the same type by creating multiple instances of the SB.
Implementation
SBs contain an SB header and footer, which are functions included in the rexhip library. The header marks the start of the SB, and all mb_query calls that follow it are tagged as belonging to that device. The footer closes the station context. Both the header and the footer take an mb_query instance and a tag of type mb_station_block_udt as arguments.

As previously mentioned, there are many pre-configured SBs in the rexhip library, as well as the header and footer functions that allow the creation of custom SBs.
The Station Block UDT
The tag of type mb_station_block_udt in an SB provides useful information and configuration options for a device’s queries and is one of the primary reasons to use SBs.

Configuration (conf):
- skip_code: Both the station block header and footer immediately return, preventing all query scheduling, execution, and result handling for that station.
- disable: Soft stop for the SB handled by the footer, allowing the station to complete its normal end-of-cycle logic before it stops participating further.
Table 4: skip_code vs disable functionality
| skip_code | disable | |
| Checked in header | Yes | No |
| Checked in footer | Yes | Yes |
| Stops header logic | Yes | No |
| Stops footer logic | Yes | No |
| Allows logging | No | Yes |
| Allows graceful finish | No | Yes |
| Typical intent | Hard kill | Controlled stop |
- read_only: Only allows read queries to be executed.
- max_comm_error: The number of consecutive communications faults that must occur before a communication error is thrown.
- exec_x_quries: The number of queries in the SB to be executed in each loop.
The “out” struct provides information about the station’s current operational status rather than historical data.
- error: General error flag which stays true until all queries in the SB complete successfully in one cycle.
- error_comm: True if consecutive communication errors exceed max_comm_error.
- finish: True for one scan when the SB passes execution to the next device.
The “log” struct provides historical, cumulative data about query execution and error conditions for diagnostics and analysis.
- connected_time: Timestamp of when a device last regained communication.
- disconnected_time: Timestamp of when a device last lost communication.
- done_cnt: The number of successful queries.
- error_comm_cnt: The number of communication errors.
- error_exception_cnt: The number of non-communication-related errors.
- error_status: The error status code of the most recent failed query.
- error_data_addr: The data address associated with the most recent failed query.
The “z” struct is used for the internal workings of the rexhip functions and is not intended for logic, diagnostics, or configuration.
Station Block Troubleshooting Tools
The mb_station_block_udt “log” struct allows engineers to quickly identify communication issues and respond appropriately. The following examples demonstrate how it could be used to troubleshoot faulty device communications.
Table 5: Example “log” Struct Use Cases
| Tag | Use Cases |
| connected_time | Identifying if a device is repeatedly dropping and reconnecting. |
| disconnected_time | Identifying if a device is repeatedly dropping and reconnecting. Correlating communication loss with external events, such as equipment being powered down. |
| done_cnt | Confirming the device is completing successful queries. Estimating how quickly queries are being completed. |
| error_comm_cnt | Confirming the device is attempting to communicate. Identifying errors as communication errors. |
| error_exception_cnt | Confirming the device is attempting to communicate. Identifying errors as non-communication Modbus exceptions. |
| error_status | Identifying the specific type of error affecting a device. |
| error_data_addr | Identifying the exact misconfigured register/coil causing errors. |
Modbus RTU Setup
The Modbus Master Block
The rexhip library includes two RTU master function blocks, mb_master_1200_ctrl and mb_master_1500_ctrl, for Siemens 1200 and 1500 PLCs, respectively. This guide will use a 1200-series PLC as an example. The 1500-series master block includes an additional operating_mode input that is mapped to the operating mode of the underlying comm_load instruction.
The master control block takes inputs for the hardware ID and baud rate, and an input/output of type mb_query. In the instance data of the master block, the static struct “conf” contains the parity, retries, timeout gain, and timeout offset parameters.

Timeout
The timeout is automatically calculated as follows:
Timeout = (total bits transmitted ÷ baud rate ) x timeout gain + timeout offset
The result is clamped to 5 ms ≤ result ≤ 65 s.
This calculation scales the timeout based on the amount of information being transmitted. Setting gain = 0 disables auto-calculation and uses the offset as a fixed timeout.
Integrating Queries with the Master Block
Below is an example of one way to load queries into a Modbus master block using station blocks.
This example will use the fbModbus function block shown above, and its instance memory data block “instance_fbModbus.” It will also use two pre-configured station blocks from the rexhip library–AUMA Valve and ABB AquaMaster 3 flowmeter.


OB1 (Main) will call fbModbus, which holds the Modbus master block, and fbDevices, which holds the station blocks.
This example system will have three valves and two flowmeters, so three valve station blocks and two flowmeter station blocks are called in fbDevices. Each one is given a unique Modbus address, and mb_query is set to “instance_fbModbus.mb_query” for all five devices.


The Path of the Query
- The query starts in an SB where it is configured, and is then passed out of the SB.
- It is then passed to fbModbus’s instance data block via fbDevices.
- It then gets loaded into the Modbus master block.
Each query loaded into the master block is automatically placed in a queue and handled one at a time by the master block.



Modbus TCP Client Setup
The rexhip library provides a wrapper for the Modbus TCP client function native to TIA Portal, mb_tcp_ctrl (which may be called mb_client_ctrl depending on the library version). The library does not provide a wrapper for the Modbus TCP server function, so if configuring a server, use the native TIA Portal function MB_SERVER.
The mb_tcp_ctrl block takes inputs for the hardware interface, connection ID (should match the server connection ID), server IP address, TCP port, timeout, and an input/output of mb_query. Below is an example of an mb_tcp_ctrl block call.

Queries are loaded into the TCP control block in the exact same way as the RTU control blocks, and query statuses can similarly be monitored in the instance memory of SBs.
Conclusion
The rexhip Modbus library provides a structured, consistent way to set up and manage Modbus RTU and TCP communications in TIA Portal. Using standard function blocks and station blocks simplifies configuration, improves readability, and makes troubleshooting easier across a wide range of devices. This approach helps engineers spend less time dealing with protocol details and more time building reliable, maintainable automation systems.
Planning a new automation project or controls upgrade?
DMC delivers Manufacturing Automation and PLC Programming solutions that help reduce downtime and improve accuracy on the factory floor.







