Modbus master (client) I/O

Modbus I/O is supported out-of-the-box via TCP and RTU (via RS485 serial ports).

Note

The described functionality requires modbus crate feature.

Block configuration

Modbus I/O should be defined in PLC config YAML as:

io:
- id: someblock # id must be 14 chars as max
  kind: modbus
  config:
    proto: tcp # or rtu
    path: host:port # e.g. 192.168.1.5:503
    # for RTU e.g.:
    #path: /dev/ttyS0:9600:8:N:1
    # the port reconnects if there are no operations within the timeout
    timeout: 5
    # frame delay, for serial port only, in seconds
    # the default is 0.1 (100ms)
    #frame_delay: 0.1
  input:
    # input block configurations (array)
  output:
    # output block configurations (array)

A single I/O per target device should be used. It can contain any number of input and output blocks which can have different synchronization intervals.

Register codes

  • hN holding register, 16-bit (e.g. h100 - holding register #100)

  • iN input register, 16-bit

  • cN coil, boolean

  • dN discrete input, boolean

Input blocks

Modbus registers are read in bulk blocks and then parsed to context according to defined maps.

Input blocks should be defined as:

io:
- id: someblock # id must be 14 chars as max
  kind: modbus
  config:
    # .....
  input:
  - reg: h0-9 # get the first 10 16-bit holding registers
    unit: 0x01 # remote device unit ID
    map:
    - offset: 0
      target: var1 # a context variable
    - offset: 2
      target: var2 # another variable
    sync: 500ms
    # shift I/O loop if required
    #shift: 100ms
  - reg: c0-1
    unit: 0x01
    map:
    - target: flag1
    - offset: 1
      target: flag2
    sync: 1s
    # shift I/O loop if required
    #shift: 100ms
  output:
    # ....

Another way to specify the number of registers to read:

- reg: h0
  number: 10 # 10 registers, starting from h0

The offset 0 can be omitted. Another way to specify offset is =N, e.g. =100. This means the offset is absolute, e.g. if a block starting from h100 (holding register #100) is fetched, =102 means 2nd register (starting from zero) in the block but 102th absolute holding register.

The field sync is mandatory and specifies how frequently the block must be synchronized.

The I/O module automatically gets the required number of registers in block and tries to convert them both to single variables and to arrays. Coils and discrete inputs can be synchronized with boolean variables only.

Float numbers can be directly synchronized if IEEE754 encoding is used on the target device. Otherwise it is necessary to define a temporary context variable and parse it in PLC programs.

Output blocks

It is highly recommended to write Modbus registers in bulk blocks as well.

Output blocks should be defined as:

io:
- id: someblock # id must be 14 chars as max
  kind: modbus
  config:
    # .....
  input:
    # .....
  output:
  - reg: h10-19 # write 10 16-bit holding registers starting from h10
    unit: 0x01 # remote device unit ID
    map:
    - offset: 0
      target: var3 # a context variable to put
    - offset: 2
      target: var4 # another variable to put
    sync: 500ms
    # shift I/O loop if required
    #shift: 100ms
  - reg: c0-1
    unit: 0x01
    map:
    - target: out1
    - offset: 1
      target: out2
    sync: 1s
    # shift I/O loop if required
    #shift: 100ms

Another way to specify the number of registers to write:

- reg: h10
  number: 10 # 10 registers, starting from h10

The field sync is mandatory and specifies how frequently the block must be synchronized.

Coils and discrete inputs can be synchronized with boolean variables only.

Float numbers can be directly synchronized if IEEE754 encoding is used on the target device. Otherwise it is necessary to define a temporary context variable and prepare it in PLC programs.

IEEE-754 endianness

Modbus is generally a big-endian protocol, however there is no strict standard how to store IEEE-754 float numbers.

Historically Bohemia Automation products have little-endian IEEE-754 by default, as this representation is widely popular in Europe. However synchronized equipment may provide data packed in IEEE-754 big-endian or require big-endian registers to be set.

To solve this problem, rPLC provides a special trait for both f32 and f64, called SwapModbusEndianess, which can be used in PLC programs:

use rplc::io::modbus::SwapModbusEndianess;

// for input data
let reg_in = ctx.reg_in.to_swapped_modbus_endianness();
// for output data
ctx.reg_out = value.to_swapped_modbus_endianness();