T3000 Modbus programming commands

T3000 uses a specialized programming framework that extends the standard Modbus commands for advanced functionality like floating-point register reading, block writing, and reading Modbus registers. This framework allows the master device to send standard Modbus commands to the slave device with additional operations. Below is a detailed breakdown of the specific commands and concepts used in T3000 programming.

In the following example, VAR1 is a user-defined variable in T3000, which stands for VARIABLE 1. It is defined under the VARIABLES tab in the T3000 software, similar to INPUTS and OUTPUTS (e.g., IN1, OUT2, etc.). Users can rename these variables for better readability.

Reading Floating Point Registers

T3000 enables reading Modbus registers in different formats, including floating-point values with various byte orders. This operation is typically used to access registers that store floating-point data.

  • Example:
VAR100 = 1.2.MB_REG_FLOAT_ABCD33 
VAR101 = 1.2.MB_REG_FLOAT_CDAB44
VAR102 = 1.2.MB_REG_FLOAT_BADC55
VAR103 = 1.2.MB_REG_FLOAT_DCBA66
  • Explanation:
    • 1 represents the Master device panel number (the device sending the Modbus request).
    • 2 represents the Slave device Modbus address (the device being queried).
    • MB_REG_FLOAT_ABCD33 refers to reading a floating-point register at address 33 with the byte order ABCD. The same applies for other formats such as CDAB, BADC, and DCBA, depending on the specific data format of the slave device.

Reading Modbus Registers

T3000 allows reading values from Modbus registers. This can be used to retrieve the data stored in various types of registers.

  • Example:
VAR104 = 1.2.MB_REG7  
  • Explanation:
    • This command reads the value stored in register 7 of the slave device with address 2.
    • You can modify the register address (MB_REG7) as needed to read other registers in the system.

Writing via Modbus Blocks (MB_BLOCKWRITE)

T3000 supports block writing to Modbus registers, which allows writing multiple registers in a single operation. This utilizes the Modbus Write Multiple Registers command to efficiently handle large amounts of data.

  • Example:
VAR1 = MB_BLOCKWRITE ( 2 , 100 , 5 , 10 , 20 , 30 , 40 , 50 )
  • Explanation:
    • 2 represents the Slave device Modbus address (target device).
    • 100 is the start Modbus register address (the first register where data will be written).
    • 5 indicates the length of the write operation, meaning we are writing to 5 registers starting from address 100.
    • The remaining values (10, 20, 30, 40, 50) are the data values that will be written to the registers.
    • Note: The maximum number of data values you can write in a single block is 25.
    • This operation uses the Write Multiple Registers Modbus command, which allows writing multiple registers at once.

Reading Unsigned Integers

T3000 can also read multiple registers and combine them to form larger data types like unsigned integers. This can be done by combining two 16-bit registers to create a single 32-bit integer.

  • Example:
VAR3 = 1.2.MB_REG3 * 65536 + 1.2.MB_REG4
  • Explanation:
    • MB_REG3 holds the high part of the unsigned integer (multiplied by 65536), and MB_REG4 holds the low part.
    • The result is a 32-bit unsigned integer.

Reading Coils (Function Code 0x01)

Coils are typically used to represent the on/off states of a device (e.g., open/closed, on/off). This command uses Function Code 0x01 to read the status of one or more coils.

  • Example:
VAR107 = 1.2.MB_COIL7
  • Explanation:
    • 1 represents the Master device panel number (the device sending the Modbus request).
    • 2 represents the Slave device Modbus address (the device being queried).
    • MB_COIL7 refers to reading Coil 7 at slave device address 2.
    • This command uses Function Code 0x01, which is used to read the status of coils (such as on/off or open/closed states).

Reading Discrete Inputs (Function Code 0x02)

Discrete Inputs are typically used to represent the status of input signals from devices. They are similar to coils but are read-only, meaning you cannot write to them. This command uses Function Code 0x02 to read the status of one or more discrete inputs.

  • Example:
VAR108 = 1.2.MB_DISINPUT7
  • Explanation:
    • 1 represents the Master device panel number.
    • 2 represents the Slave device Modbus address.
    • MB_DISINPUT7 refers to reading Discrete Input 7 at slave device address 2.
    • This command uses Function Code 0x02, which is used to read the status of discrete inputs.

Reading Input Registers (Function Code 0x04)

Input Registers are typically used to read data from sensors or other input devices. This command uses Function Code 0x04 to read the values of one or more input registers.

  • Example:
VAR109 = 1.2.MB_INPUTREG7
  • Explanation:
    • 1 represents the Master device panel number.
    • 2 represents the Slave device Modbus address.
    • MB_INPUTREG7 refers to reading Input Register 7 at slave device address 2.
    • This command uses Function Code 0x04, which is used to read values from input registers, typically used to retrieve data from sensors or other input devices.

Writing to Modbus Registers

  • Example:
1.2.MB_REG3 = VAR1 / 65536
  • Explanation:
    • 1 represents the Master device panel number (the device sending the Modbus command).
    • 2 represents the Slave device Modbus address (the target device).
    • MB_REG3 refers to writing the value to Register 3 at slave device address 2.
    • The value of VAR1 is divided by 65536 and written to the register. This operation writes the high-level part of VAR1 into the register.
    • This command uses Write Single Register (Function Code 0x06), which is used to write a value to a single Modbus register.

Specialized Device Addressing

  • Example:
259 VAR98 = 199.4.MB_REG55
  • Explanation:
    • 199 represents the last number of the IP address of the device to be read (e.g., the IP address 192.168.0.199).
    • 4 represents the Slave device Modbus address (the device being queried).
    • MB_REG55 refers to reading Register 55 at slave device address 4.
    • This command allows the Master device to read from a specific device by its IP address and Modbus register. The IP here actually refers to other devices with IP address 199 in the same network segment as this device. When the panel number of this device is also 199, the device should use the panel number as the first priority.

These are great examples but some more detail is needed for me to fully understand how to do this on my project.

  1. The block write example seems to be hard coded to write 10,20,30,40,50 to registers 100,101,102,103,104 but what is 60 and 70?
  2. What does VAR1 represent if the values we are writing are constants programmed into the line of code?
  3. How would we add a variable in place of the position the “10” is at so that a setpoint could be written based on the user input variable “USERSTPT”?

The specialized device addressing at the end also needs more details.
What if the IP address you want to write to ends with 1? Then the specialized address code in your example is 1.4.MB_REG55 and your Reading Modbus Registers syntax example is 1.2.MB_REG7 so how would it know to use IP instead of modbus port?

Thanks for good feedback. The modbus commands we have were built up as a patchwork over the years, its due for a revamp.

I asked copilot for some suggestions and this is what has come up. Have a look and send some feedback before we get started:

Modbus Function Code-Based Commands

1. Function Code 01: Read Coils

  • MB_READ_COILS(mode, device, start_address, count) : Reads the status of multiple coils.

2. Function Code 02: Read Discrete Inputs

  • MB_READ_DISCRETE_INPUTS(mode, device, start_address, count) : Reads the status of multiple discrete inputs.

3. Function Code 03: Read Holding Registers

  • MB_READ_HOLDING_REGISTERS(mode, device, start_address, count) : Reads values from multiple holding registers.

4. Function Code 04: Read Input Registers

  • MB_READ_INPUT_REGISTERS(mode, device, start_address, count) : Reads values from multiple input registers.

5. Function Code 05: Write Single Coil

  • MB_WRITE_SINGLE_COIL(mode, device, address, status) : Writes the status of a single coil (on/off).

6. Function Code 06: Write Single Register

  • MB_WRITE_SINGLE_REGISTER(mode, device, address, value) : Writes a value to a single holding register.

7. Function Code 15: Write Multiple Coils

  • MB_WRITE_MULTIPLE_COILS(mode, device, start_address, statuses[]) : Writes statuses (on/off) to multiple coils.

8. Function Code 16: Write Multiple Registers

  • MB_WRITE_MULTIPLE_REGISTERS(mode, device, start_address, values[]) : Writes values to multiple holding registers.

9. Function Code 07: Read Exception Status

  • MB_READ_EXCEPTION_STATUS(mode, device) : Reads the exception status of the device (for Modbus RTU slaves only).

10. Function Code 08: Diagnostics

  • MB_DIAGNOSTICS(mode, device, sub_function, data[]) : Executes standard diagnostic functions.

Example Usage

Coil Operations

' Read 10 coils starting from address 0
coil_statuses[] = MB_READ_COILS("IP", "192.168.1.100:502", 0, 10)

' Write to a single coil
MB_WRITE_SINGLE_COIL("RTU", 1, 5, 1) ' Turn coil on

Register Operations

' Read 5 holding registers starting at address 300
holding_values[] = MB_READ_HOLDING_REGISTERS("IP", "192.168.1.100:502", 300, 5)

' Write a value to a single register
MB_WRITE_SINGLE_REGISTER("RTU", 2, 400, 1234)

Thank you very much for your questions, I updated this post based on them.

1.You are correct in noting that the example was initially hardcoded with values 10, 20, 30, 40, 50. The numbers 60 and 70 were mistakenly included and have been corrected.

  1. VAR1 is a user-defined variable in T3000, which stands for VARIABLE 1 . It is defined under the VARIABLES tab in the T3000 software, similar to INPUTS and OUTPUTS (e.g., IN1 , OUT2 , etc.). Users can rename these variables for better readability.

  2. I don’t know if I understand it correctly. Do you want to use it like this?

10 USERSTPT = 30
20 1.2.MB_REG102 = USERSTPT

Here I assign 30 to the variable USERSTPT, and then pass it to the 102nd register of the sub-device modbus address 2 under the master device panel number 1, and this register is the setpoint of the sub-device.

The specialized device addressing is indeed unclear. The IP here actually refers to other devices with IP address 199 in the same network segment as this device. When the panel number of this device is also 199, the device should use the panel number as the first priority.

Our team will report back on your current project and present state of the modbus commands.

Moving forward, I would like to see a more unified and simpified set of commands. Here’s what copilot and I have come up with, now including floats and endian byte order features:

Unified Modbus Command Set (With Mode in Device Parameter)

1. Device Definition

Define devices with their mode and connection details included in the parameter:

basic

MB_DEFINE_DEVICE(name, connection_details)
  • name: Alias for the device (e.g., “Motor1”).
  • connection_details:
    • For RTU: "RTU:COM1,9600,N,1,2" (includes mode and RTU-specific details like port and device ID).
    • For IP: "IP:192.168.1.100:502" (includes mode and IP-specific details like IP address and port).

Commands List

2. Single Register Operations

  • MB_READ_REGISTER(device, address): Reads a single holding register.
  • MB_WRITE_REGISTER(device, address, value): Writes a value to a single holding register.

3. Block Register Operations

  • MB_READ_BLOCK(device, start_address, count): Reads multiple contiguous holding registers.
  • MB_WRITE_BLOCK(device, start_address, values[]): Writes to multiple contiguous holding registers.

4. Coil Operations

  • MB_READ_COIL(device, address): Reads the status of a single coil.
  • MB_WRITE_COIL(device, address, status): Writes the status of a single coil.
  • MB_READ_COILS(device, start_address, count): Reads the status of multiple coils.
  • MB_WRITE_COILS(device, start_address, statuses[]): Writes statuses (on/off) to multiple coils.

5. Discrete Inputs and Input Registers

  • MB_READ_DISCRETE_INPUT(device, address): Reads the status of a single discrete input.
  • MB_READ_INPUT_REGISTER(device, address): Reads a single input register.
  • MB_READ_INPUT_BLOCK(device, start_address, count): Reads multiple contiguous input registers.

Extended Commands for Floats and Byte Order

6. Floating-Point Data

  • MB_READ_FLOAT(device, address, byte_order): Reads a 32-bit floating-point value (two registers).
  • MB_WRITE_FLOAT(device, address, value, byte_order): Writes a 32-bit floating-point value (two registers).

7. Multi-Byte Data

  • MB_READ_UINT32(device, address, byte_order): Reads a 32-bit unsigned integer (two registers).
  • MB_WRITE_UINT32(device, address, value, byte_order): Writes a 32-bit unsigned integer (two registers).
  • MB_READ_INT32(device, address, byte_order): Reads a 32-bit signed integer (two registers).
  • MB_WRITE_INT32(device, address, value, byte_order): Writes a 32-bit signed integer (two registers).

8. Byte Order (Endianness)

Supports these formats:

  • "BE": Big-endian (default Modbus order).
  • "LE": Little-endian.
  • "WSBE": Word-swapped big-endian.
  • "WSLE": Word-swapped little-endian.

Example Device Setup

basic

' Define an RTU device
MB_DEFINE_DEVICE("Pump1", "RTU:COM1,9600,N,1,5")

' Define an IP device
MB_DEFINE_DEVICE("ValveController", "IP:192.168.0.10:502")

Example Usage

Standard Operations

basic

' Read a single register from RTU device
value = MB_READ_REGISTER("Pump1", 100)

' Write to a register on IP device
MB_WRITE_REGISTER("ValveController", 200, 1250)

Floating-Point and Byte Order

basic

' Read a floating-point value in big-endian format
temperature = MB_READ_FLOAT("Pump1", 300, "BE")

' Write a floating-point value in little-endian format
MB_WRITE_FLOAT("ValveController", 400, 123.45, "LE")

32-bit Integer Operations

basic

' Read a 32-bit unsigned integer in word-swapped little-endian format
total_count = MB_READ_UINT32("Pump1", 500, "WSLE")

' Write a 32-bit signed integer in big-endian format
MB_WRITE_INT32("Pump1", 501, -456789, "BE")

Benefits of This Final Design

  1. Unified Structure: Combines IP and RTU support with mode embedded in device configuration.
  2. Float and Byte Order Support: Handles multi-register values like floats and 32-bit integers seamlessly.
  3. Extensibility: Focuses on core Modbus function codes but allows for future additions like advanced features or security.