VisualHMI LUA Modbus implementation with one master and multiple slaves: practical examples and pitfalls of start_read/select_slave

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

Previously, we discussed configuring screens with Modbus master and slave in engineering projects. However, when encountering "one master and multiple slaves + complex logic" (such as reading the sum of electricity from two slaves, writing to registers based on conditions, and batch writing to coils), relying solely on binding variables to the screen is insufficient. LUA scripts must be used to take over communication. This article will thoroughly explain the Modbus-related LUA APIs, with code examples from real-world projects in the documentation. I have added annotations and pitfall warnings from an engineer's perspective.

⚠️ Scope of application:HMI&M&Dx series. The example involves two Modbus slaves with station numbers 10 and 20, but the logic is also applicable to FX2N/FX3U master-slave configurations.

1. Core Communication API

1.1 start_read —— Background automatic polling (most commonly used)

This is a scheduling function for periodically reading slave registers in master mode. After calling, the system adds it to the communication queue, automatically sends requests every cycle, and writes the returned data into the HMI memory image. You can directly read the cache usingget_uint16/get_float, withno communication delay..

start_read(index, vtype, addr, quantity, cycle, cycle_run, mode)
ParametersTypeDescription
indexnumberTask index 0~127, used when stopping read
vtypenumberData type VT_3x / VT_4x, etc.
addrnumberRegister address
quantitynumberRead quantity 1~120
cyclenumberPolling cycle multiplier, 0=read every cycle
cycle_runnumberExecution count within the cycle (0-based, must be < cycle)
modenumber0=continuous reading (default) / 1=read only once
💡 Scheduling essence:cycle/cycle_run is used to distinguish between high and low frequency variables. For high frequency variables (speed, temperature), cycle=0, read every cycle; for low frequency variables (accumulated quantity, status word), cycle=5, cycle_run=4, read once every 5 cycles,reducing bus load. Example:
start_read(0, VT_4x, 0x1000, 10)→ read 4x1000~1009 every cycle
start_read(1, VT_4x, 0x2000, 10, 3, 2)→ Every 3 cycles, read 4x2000~2009 in the 3rd cycle

1.2 Other management functions

  • stop_read(index): Stop a single polling task, cache retains the last value
  • stop_all_read(): One-click stop all, suitable for reset/emergency stop
  • set_auto_read(en): 1=Enable automatic reading of screen binding variables (default), 0=Rely only on scriptstart_readTake over, suitable for redundancy removal in high bandwidth scenarios

1.3 select_slave —— Switch slave (critical)

Under multiple slaves,select_slave(slave_id)temporarily specify which slave to access nextget_xxx/set_xxx.Note that slave_id is the index of the slave list, not the Modbus station number!

TerminologyMeaningExample
Slave index slave_idHMI internal list index 0,1,20 = first slave
Modbus station numberDevice physical address10, 33
⚠️ Most common mistake:slave_id=0 corresponds to station number 10, and slave_id=1 corresponds to station number 33. This mapping is determined in the project configuration. If the index is filled incorrectly in the script, it will read from a different slave, and you may spend a long time investigating, thinking it's a protocol issue.

II. LUA reading registers (with online judgment)

Before reading, two things cannot be omitted:Determine if the slave is online + Select the correct slave. The online status is stored in the corresponding bit of the system variableVT_LW 0x01A3.

-- 读保持寄存器 4x
function mb_read_reg_03(slave, addr)
  local onlineState = get_uint16(VT_LW, 0x01A3)   -- 从机在线状态字
  if ((onlineState >> slave) & 0x01) == 0x01 then  -- 该从机在线
    select_slave(slave)                            -- 切到这个从机
    return get_uint16(VT_4x, addr)                 -- 读缓存
  else
    return false
  end
end

-- 读两个从机电量求和,写回 LW 区
function on_run(screen)
  local Energy1 = mb_read_reg_03(0, 0x0000)
  local Energy2 = mb_read_reg_03(1, 0x0000)
  if Energy1 ~= false and Energy2 ~= false then
    set_uint16(VT_LW, 0x1000, Energy1 + Energy2)
  end
end

III. LUA Write Registers

-- 写单个保持寄存器 0x06
function mb_write_reg_06(slave, addr, value)
  local onlineState = get_uint16(VT_LW, 0x01A3)
  if ((onlineState >> slave) & 0x01) == 0x01 then
    feed_dog()              -- 喂狗,防脚本卡死看门狗复位
    select_slave(slave)
    set_uint16(VT_4x, addr, value)
    return true
  else
    return false
  end
end
⚠️ Important: Before writing, be sure to call feed_dog().The LUA script runs in the real-time task of the screen. If the watchdog is not fed for a long time, it will be killed as a dead loop and the screen will restart. For any looping or time-consuming logic, add feed_dog in the interval.

An example of weighing calibration (three calibration modes), showingon_updatebranching writing based on the LW variable in the callback:

function on_update(slave, vtype, addr)
  if vtype == VT_LW and addr == 0x1004 then
    local mode = get_uint16(VT_LW, 0x1003)
    if mode == 0 then
      mb_write_reg_06(0, 0x1002, 57)            -- 零点校准
    elseif mode == 1 then
      local w = get_uint16(VT_LW, 0x1005)        -- 砝码校准
      mb_write_reg_06(0, 0x1002, w)
    elseif mode == 2 then
      local c = get_uint16(VT_LW, 0x1006)        -- 实物校准
      local s = get_uint16(VT_LW, 0x1007)
      if c > 0 and s > 0 then
        mb_write_reg_06(0, 0x1002, (s*w)//c)
      end
    end
  end
end

IV. Batch Write Coils (0x0F Package)

The minimum unit for batch writing coils in VisualHMI is 16-bit alignment. The following function is for the large color package (non-system API, written by myself), demonstrate how to write 14 coil values together:

mb_write_coil_15(slave_id, addr, coilsTb)
-- slave_id: 从站索引; addr: 起始地址; coilsTb: 线圈值表
Debugging tips:For online verification, use Modbus Slave software + virtual serial port pair, with the screen acting as the master and the computer as the slave, to observe real-time changes in total voltage/total electricity. This is much faster than directly connecting to a real device.

This article is compiled from the "Modbus Application" section of the VisualHMI development documentation LUA tutorial by Guangzhou Dacai Technology. The code and API are faithfully preserved from the original text. The copyright belongs to Dacai Technology. Here, we add engineer's notes and key annotations for learning and reference purposes only.

Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.