VisualHMI - Modbus一主多从
本章节将介绍主从模式下(Modbus/FX2N/FX3U),读写PLC/驱动器的寄存器
以Modbus 为例,项目创建两个从站,第1个从站的站号为10,第2个从站的站号为20,如下所示:
- Lua读PLC/驱动器的寄存器
- Lua写PLC/驱动器的寄存器
- Lua批量写寄存器
适用范围:VisualHMI - HMI&M系列
例程下载链接:ViusalHMI - 一主多从(点击下载)
1. Lua API说明
1.1. start_read(index,vtype, addr,quantity)
指定读取多个连续的变量,系统自动发指令读取变量地址
[!note|tip:注意] 1.只能用于主机模式,例如Modbus-Master、FX2N等
2.调用一次,屏幕会周期性发指令请求,停止需要调用stop_read
3.该函数没有返回值,需要配合get_xx获取寄存器的值
4.当前画面用到的变量地址或者有配置告警、资料采集,系统会自动读取,无需使用start_read。
5.若当前画面的控件绑定变量地址或者有配置告警、资料采集,脚本调用的start_read和前者有交叉部分,系统会优化读取指令,重新整合指令请求
- index:索引,通常和停止读取搭配stop_read
- vtype:数据类型
- addr:开始读取的地址
- quantity:读取的个数,最大120
1.2. stop_read(index)
停止读取多个连续的变量
[!note|tip:注意] 只能用于主机模式,例如Modbus-Master、FX2N等
- index:索引,通常和开始读取搭配start_read
1.3. stop_all_read()
停止所有start_read开启的读取指令
[!note|tip:注意] 只能用于主机模式,例如Modbus-Master、FX2N等
1.4.set_auto_read(en)
使能画面控件绑定的寄存器自动读取
[!note|tip:注意] 1.只能用于主机模式,例如Modbus-Master、FX2N等
2.set_auto_read(0),禁止后,用start_read 控制读取
3.画面的控件绑定寄存器,默认自动读取
- en:0-画面禁止自动读取,1-画面自动读取
1.5.select_slave(slave_id)
选择从站地址,用于多从机模式,如MODBUS、FX2N协议
- slave_id:从机索引
2.后台读取
2.1.Lua 脚本
本文以Modbus为例,屏幕当主站。若需要后台一直轮询读取某些参数(任意界面下),可以在脚本调用start_read,只需要调用一次,将一直轮询读取,直到调用stop_read 或 stop_all_read()停止轮询,如下所示:
function on_init()
select_slave(0)--选择第1个从站
start_read(1,VT_4x, 0x0000, 3)--读取保持寄存器,从0x0000开始,连续3个寄存器
start_read(2,VT_0x, 0x0000, 14)--读取线圈,从0x0000开始,连续14个寄存器
redraw()
select_slave(1)--选择第2个从站
start_read(3,VT_4x, 0x0000, 3)--读取保持寄存器,从0x0000开始,连续3个寄存器
start_read(4,VT_4x, 0x1000, 7)--读取保持寄存器,从0x1000开始,连续7个寄存器
redraw()
end
若想全部通过脚本start_read 读取寄存器,则可以关闭画面控件绑定读取方式,在on_init() 调用 set_auto_read(0)
[!note|tip:注意] 若没有配置 set_auto_read(0)。轮询读取寄存器可以通过以下方式发出指令
1.脚本调用start_read
2.界面上的控件绑定从站寄存器地址
若这两种方式存在,系统会优化请求指令,合并交叉寄存器部分,减少重复的指令
function on_init()
set_auto_read(0)--关闭控件读取
select_slave(0)--选择第1个从站
start_read(1,VT_4x, 0x0000, 3)--读取保持寄存器,从0x0000开始,连续3个寄存器
start_read(2,VT_0x, 0x0000, 14)--读取线圈,从0x0000开始,连续14个寄存器
redraw()
select_slave(1)--选择第2个从站
start_read(3,VT_4x, 0x0000, 3)--读取保持寄存器,从0x0000开始,连续3个寄存器
start_read(4,VT_4x, 0x1000, 7)--读取保持寄存器,从0x1000开始,连续7个寄存器
redraw()
end
2.2.运行预览
运行虚拟屏,屏幕发出的读取指令如下所以:
0A 01 00 00 00 10 3C BD -> start_read(2,VT_0x, 0x0000, 14)
0A 03 00 00 00 03 04 B0 -> start_read(1,VT_4x, 0x0000, 3)
14 03 00 00 00 03 07 0E -> start_read(3,VT_4x, 0x0000, 3)
14 03 10 00 00 07 02 0D -> start_read(4,VT_4x, 0x1000, 7)
3.Lua 读寄存器
3.1.Lua 脚本
Lua脚本读取从机寄存器值,通常用于条件判断、计算和等。步骤如下所示
- 在脚本获取该寄存器时,确保后台有轮询请求,保证获取的值是最新的
- 获取寄存器前,要需要判断从机是否在线
- 获取寄存器前,需选择对应的从机
--读取保存寄存器
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
假设需要读取从机1/从机2的电量、电量,并计算总电压电量。如下所示
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
local vol1 = mb_read_reg_03(0, 0x0001)
local vol2 = mb_read_reg_03(1, 0x0001)
if vol1 ~= false and vol2 ~= false
then
set_uint16(VT_LW, 0x1001, vol1 + vol2)
end
end
3.2.运行预览
创建虚拟串口对,用Modbus slave和虚拟屏联机,如下所示,总电压、总电压实时变化:
4.Lua 写寄存器
4.1.Lua 脚本
Lua脚本写从机寄存器值,通常在某个条件下或计算后,设置对应寄存器。步骤如下所示
- 获取寄存器前,要需要判断从机是否在线
- 获取寄存器前,需选择对应的从机
--设置保存寄存器
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
假设一个称重业务,一共有三种校准模式,如下所示
- 零点校准:实重 = 57
- 砝码校准:实重 = 砝码重量
- 实物校准:实重=标定系数*砝码重量 // 保存码
function on_update(slave,vtype,addr)
if vtype == VT_LW
then
if 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
--实重 = 砝码重量
print('val == 1')
local weightVal = get_uint16(VT_LW, 0x1005) -- 砝码重量
mb_write_reg_06(0, 0x1002, weightVal)
elseif mode == 2
then
--if (标定系数>0) && (保存码>0)
--then
-- 实重=标定系数*砝码重量 // 保存码
--end
local coeffi = get_uint16(VT_LW, 0x1006) --标定系数
local saveCode = get_uint16(VT_LW, 0x1007) --保存码
if coeffi > 0 and saveCode > 0
then
local weightVal = get_uint16(VT_LW, 0x1005) -- 砝码重量
mb_write_reg_06(0, 0x1002, (saveCode*weightVal)//coeffi )
end
end
end
end
end
4.2.运行预览
创建虚拟串口对,用Modbus slave和虚拟屏联机,如下所示,选择不同的校准方式和校准参数,校准结果实时变化:
5.Lua 批量写线圈
5.1.Lua 脚本
VisualHMI批量写线圈最小单位是短整型,16个线圈对齐。本章节封装了批量写线圈的函数,直接调用以下接口
mb_write_coil_15(slave_id, addr, coilsTb)
- slave_id:从站号索引
- addr:起始地址
- coilsTb:线圈值,字表
[!note|tip:注意] mb_write_coil_15 该函数为自行封装api,非系统api
假设一个业务逻辑,线圈起始地址为0x0000,设置连续14个线圈值,如下所示
coil | c0 | c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c9 | c10 | c11 | c12 | c13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
val | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
按钮按下,执行批量设置寄存器逻辑,如下所示:
--批量写多个线圈
function mb_write_coil_15 (slave,addr,coils)
if ((addr + #coils) > 0xFFFF ) or (#coils > 120) or (#coils <= 0)
then
return
end
local onlineState = get_uint16(VT_LW,0x01A3)
if ((onlineState >> slave) & 0x01) == 0x01
then
feed_dog()
select_slave(slave)
local cnt = (#coils // 16) + (((#coils % 16) >= 0 ) and 1 or 0) -- 总共个数,16个线圈对齐
local all = cnt*16
local idx = (#(coils) + 1)
if all > 120 --标准modbus 最大读写连续寄存器<=120
then
return
end
for i = idx, all --16bit对齐,获取实际对齐部分线圈的值
do
local ret = mb_read_coil_01(slave, ((addr + i) - 1))
if ret == false
then
return false
end
coils[#coils + 1] = ret
end
local val = {}
for i = 1, cnt --合并16位数据
do
val[i] = 0x0000
for j = 0, 15
do
val[i] = set_valbit(val[i], j, coils[(i - 1)*16 + j + 1])
end
end
set_array(VT_0x, addr, val)
return true
else
return false
end
end
function on_update(slave,vtype,addr)
if vtype == VT_LW
then
if addr == 0x1004
then
......
elseif addr == 0x2000
then
local val = get_uint16(VT_LW, 0x2000) -- 批量写
if val == 1
then
--批量写入0x0000~0x000D, 14个寄存器 5B1A
mb_write_coil_15(0, 0x0000, {1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0 })
....
end
end
end
end
5.2.运行预览
创建虚拟串口对,用Modbus slave和虚拟屏联机,如下所示,点击批量写线圈按钮,0x0000~0x000D线圈对应变化
6.Lua 批量写寄存器
6.1.Lua 脚本
VisualHMI批量写保持寄存器最小单位是16位数据。本章节封装了批量写寄存器的函数,直接调用以下接口
mb_write_reg_16(slave_id, addr, regsTb)
- slave_id:从站号索引
- addr:起始地址
- regsTb:寄存器值,字表
[!note|tip:注意] mb_write_reg_16 该函数为自行封装api,非系统api
--批量写多个保存寄存器
function mb_write_reg_16 (slave,addr,regs)
local onlineState = get_uint16(VT_LW,0x01A3)
if ((onlineState >> slave) & 0x01) == 0x01
then
feed_dog()
select_slave(slave)
set_array(VT_4x, addr, regs)
return true
else
return false
end
end
function on_update(slave,vtype,addr)
if vtype == VT_LW
then
if addr == 0x1004
then
......
elseif addr == 0x2000
then
local val = get_uint16(VT_LW, 0x2000) -- 批量写
if val == 1
then
....
elseif val == 2
then
--批量写入0x1000~0x1006, 7个寄存器
mb_write_reg_16(1, 0x1000, {0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007})
end
end
end
end
6.2.运行预览
创建虚拟串口对,用Modbus slave和虚拟屏联机,如下所示,点击批量写寄存器按钮,0x1000~0x1006寄存器对应变化