跳转至

modbus - modbus 主从协议栈协议

作者:马梦阳

一、概述

Modbus 是一种串行通信协议,最初由 Modicon(现为施耐德电气旗下品牌)于 1979 年开发,用于可编程逻辑控制器(PLC)之间的通信。由于其简单、开放、免费且易于实现,Modbus 已成为工业自动化领域最广泛使用的通信协议之一,被广泛应用于工业控制、楼宇自动化、能源管理、智能仪表等场景。

该协议采用主从架构,支持串行(如 Modbus RTU/ASCII)和以太网(Modbus TCP)两种主要传输方式。并定义了线圈、离散输入、输入寄存器和保持寄存器四种数据类型,通过功能码(如读/写寄存器)实现设备间数据交换。

LuatOS 提供了 modbus 核心库,方便用户更加方便的管理和处理 Modbus 的主/从信息。

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/modbus;

modbus RTU 主站示例程序

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_master_rtu"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

-- 初始化通讯串口
-- 根据实际设备选取不同的uartid
local uartid = 1
-- 用于控制485接收和发送的使能引脚
local uart485Pin = 17
-- 打开电源(Air8000 开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
gpio.setup(16, 1)
uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)

-- 创建主站设备,Modbus RTU通信模式
-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, uartid, 115200, 3000, 2000, 1, 5000)

-- 为主站添加从站,从站ID为1,可使用modbus.add_slave(master_handler, slave_id)接口添加多个从站,最多可以添加247个
mb_slave1 = modbus.add_slave(mb_rtu, 1)
-- -- 为主站添加从站,从站ID为2
-- mb_slave2 = modbus.add_slave(mb_rtu, 2)

-- 为从站1创建数据存储区,并创建通讯消息,读取保持寄存器,默认为自动loop模式
slave1_msg1_buf = zbuff.create(1)
mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
slave1_msg1_buf:clear()

-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
-- slave1_msg1_buf = zbuff.create(1)
-- mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
-- slave1_msg1_buf:clear()

-- -- 为从站2创建数据存储区,并创建通讯消息,写入寄存器
-- slave2_msg1_buf = zbuff.create(1)
-- mb_slave2_msg1 = modbus.create_msg(mb_rtu, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
-- slave2_msg1_buf:clear()

-- 启动Modubs设备
modbus.master_start(mb_rtu)
log.info("start modbus master")

-- -- 设置通讯间隔时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯间隔时间一致,优先级更高
-- -- 设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- modbus.set_comm_interval_time(mb_rtu, 3000)

-- -- 设置通讯超时时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯超时时间一致,优先级更高
-- -- 当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
-- modbus.set_comm_timeout(mb_rtu, 2000)

-- -- 设置消息发送失败、超时重发次数
-- -- 此处设置的次数与modbus.create_master中设置的消息超时重发次数一致,优先级更高
-- -- 如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
-- modbus.set_comm_resend_count(mb_rtu,2)

-- -- 设置消息通讯周期
-- -- 仅对modbus.create_msg接口中comm_mode = modbus.LOOP模式的通信任务生效,对 modbus.EXEC 或 modbus.SINGLE 模式通信任务无效(因其不参与周期调度)
-- -- 搭配modbus.create_master/modbus.set_comm_interval_time(mb_rtu, 3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)

-- 获取所有从站状态
-- modbus.get_all_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_rtu then
        local all_slave_status = modbus.get_all_slave_state(mb_rtu)
        log.info("modbus", all_slave_status)
    else
        log.info("modbus", "mb_rtu 主站句柄无效")
    end
end, 5000)

-- 获取从站1的状态
-- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_slave1 then
        local slave1_status = modbus.get_slave_state(mb_slave1)
        if slave1_status == modbus.SLAVE_NORMAL then
            log.info("mb_slave1 从站在线且通信正常")
        elseif slave1_status == modbus.SLAVE_OFFLINE then
            log.warn("mb_slave1 从站已离线")
        elseif slave1_status == modbus.SLAVE_UNKNOWN then
            log.warn("mb_slave1 从站状态未知")
        elseif slave1_status == modbus.SLAVE_COMM_TIMEOUT then
            log.warn("mb_slave1 从站通信超时,可能即将离线")
        elseif slave1_status == modbus.SLAVE_ERROR then
            log.error("mb_slave1 从站发生错误")
        end
    else
        log.error("mb_slave1 从站句柄无效")
    end
end, 5000)

-- -- 获取从站2的状态
-- -- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- -- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
-- sys.timerLoopStart(function()
--     if mb_slave2 then
--         local slave2_status = modbus.get_slave_state(mb_slave2)
--         if slave2_status == modbus.SLAVE_NORMAL then
--             log.info("mb_slave2 从站在线且通信正常")
--         elseif slave2_status == modbus.SLAVE_OFFLINE then
--             log.warn("mb_slave2 从站已离线")
--         elseif slave2_status == modbus.SLAVE_UNKNOWN then
--             log.warn("mb_slave2 从站状态未知")
--         elseif slave2_status == modbus.SLAVE_COMM_TIMEOUT then
--             log.warn("mb_slave2 从站通信超时,可能即将离线")
--         elseif slave2_status == modbus.SLAVE_ERROR then
--             log.error("mb_slave2 从站发生错误")
--         end
--     else
--         log.error("mb_slave2 从站句柄无效")
--     end
-- end, 5000)

-- -- 手动执行通信任务
-- -- 每隔5秒执行一次mb_slave1_msg1消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.create_msg接口中设置为手动模式;成功返回true,其他情况返回false
-- sys.timerLoopStart(function()
--     local status = modbus.exec(mb_rtu, mb_slave1_msg1)
--     log.info("msg", status)
-- end,5000)

-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
-- -- 将在3分钟后删除从站1(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
-- sys.timerStart(function()
--     local status = modbus.remove_slave(mb_rtu, mb_slave1)
--     log.info("modbus", "slave1 remove after 3 minutes")
--     log.info("remove", status)

-- -- 移除从站后,5秒后重新启动Modbus主站
--     sys.timerStart(function()
--         modbus.master_start(mb_rtu)
--         log.info("modbus", "Modbus master restarted after slave removal")
--     end, 5000)
-- end, 180000)

-- -- 删除从站消息
-- -- 第三个参数为空时,则表示删除指定从站的所有消息
-- -- 可在主站运行(master_start 后)或停止状态下调用,均有效
-- modbus.remove_msg(mb_rtu, mb_slave1, mb_slave1_msg1)

-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
sys.timerLoopStart(function()
    -- 检查 mb_slave1 从站状态
    local status = modbus.get_slave_state(mb_slave1)
    -- status 为 0 时表示从站通信正常
    if status == 0 then
        -- 读取缓冲区数据
        -- 重置指针到起始位置
        slave1_msg1_buf:seek(0)

        -- 读取4个寄存器的值(每个寄存器2字节)
        local reg1 = slave1_msg1_buf:readU16()
        local reg2 = slave1_msg1_buf:readU16()
        local reg3 = slave1_msg1_buf:readU16()
        local reg4 = slave1_msg1_buf:readU16()

        -- 创建数据表
        local data = {
            addr = 1,  -- 从站地址
            fun = 3,   -- 功能码03
            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
            reg2 = reg2 / 10,
            reg3 = reg3 / 10,
            reg4 = reg4 / 10,
            timestamp = os.time()  -- 添加时间戳
        }

        -- 转换为JSON
        local json_str = json.encode(data)
        log.info("mb_slave1 Modbus数据转JSON:", json_str)
    else
        log.warn("mb_slave1 从站1状态异常:", status)
    end
end, 1000)

-- -- 将在主站开启2分钟后停止modbus主站
-- sys.timerStart(function()
--     modbus.master_stop(mb_rtu)
--     log.info("modbus", "Modbus stopped after 2 minutes")
-- end, 120000)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus ASCII 主站示例程序

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_master_ascii"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

-- 初始化通讯串口
-- 根据实际设备选取不同的uartid
local uartid = 1
-- 用于控制485接收和发送的使能引脚
local uart485Pin = 17
-- 打开电源(Air8000 开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
gpio.setup(16, 1)
uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)

-- 创建主站设备,Modbus ASCII通信模式
-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
mb_ascii = modbus.create_master(modbus.MODBUS_ASCII, uartid, 115200, 3000, 2000, 1, 5000)

-- 为主站添加从站,从站ID为1,可使用modbus.add_slave(master_handler, slave_id)接口添加多个从站,最多可以添加247个
mb_slave1 = modbus.add_slave(mb_ascii, 1)
-- -- 为主站添加从站,从站ID为2
-- mb_slave2 = modbus.add_slave(mb_ascii, 2)

-- 为从站1创建数据存储区,并创建通讯消息,读取保持寄存器,默认为自动loop模式
slave1_msg1_buf = zbuff.create(1)
mb_slave1_msg1 = modbus.create_msg(mb_ascii, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
slave1_msg1_buf:clear()

-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
-- slave1_msg1_buf = zbuff.create(1)
-- mb_slave1_msg1 = modbus.create_msg(mb_ascii, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
-- slave1_msg1_buf:clear()

-- -- 为从站2创建数据存储区,并创建通讯消息,写入寄存器
-- slave2_msg1_buf = zbuff.create(1)
-- mb_slave2_msg1 = modbus.create_msg(mb_ascii, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
-- slave2_msg1_buf:clear()

-- 启动Modubs设备
modbus.master_start(mb_ascii)
log.info("start modbus master")

-- -- 设置通讯间隔时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯间隔时间一致,优先级更高
-- -- 设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- modbus.set_comm_interval_time(mb_ascii, 3000)

-- -- 设置通讯超时时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯超时时间一致,优先级更高
-- -- 当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
-- modbus.set_comm_timeout(mb_ascii, 2000)

-- -- 设置消息发送失败、超时重发次数
-- -- 此处设置的次数与modbus.create_master中设置的消息超时重发次数一致,优先级更高
-- -- 如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
-- modbus.set_comm_resend_count(mb_ascii,2)

-- -- 设置消息通讯周期
-- -- 仅对modbus.create_msg接口中comm_mode = modbus.LOOP模式的通信任务生效,对 modbus.EXEC 或 modbus.SINGLE 模式通信任务无效(因其不参与周期调度)
-- -- 搭配modbus.create_master/modbus.set_comm_interval_time(mb_ascii, 3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)

-- 获取所有从站状态
-- modbus.get_all_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_ascii then
        local all_slave_status = modbus.get_all_slave_state(mb_ascii)
        log.info("modbus", all_slave_status)
    else
        log.info("modbus", "mb_ascii 主站句柄无效")
    end
end, 5000)

-- 获取从站1的状态
-- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_slave1 then
        local slave1_status = modbus.get_slave_state(mb_slave1)
        if slave1_status == modbus.SLAVE_NORMAL then
            log.info("mb_slave1 从站在线且通信正常")
        elseif slave1_status == modbus.SLAVE_OFFLINE then
            log.warn("mb_slave1 从站已离线")
        elseif slave1_status == modbus.SLAVE_UNKNOWN then
            log.warn("mb_slave1 从站状态未知")
        elseif slave1_status == modbus.SLAVE_COMM_TIMEOUT then
            log.warn("mb_slave1 从站通信超时,可能即将离线")
        elseif slave1_status == modbus.SLAVE_ERROR then
            log.error("mb_slave1 从站发生错误")
        end
    else
        log.error("mb_slave1 从站句柄无效")
    end
end, 5000)

-- -- 获取从站2的状态
-- -- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- -- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
-- sys.timerLoopStart(function()
--     if mb_slave2 then
--         local slave2_status = modbus.get_slave_state(mb_slave2)
--         if slave2_status == modbus.SLAVE_NORMAL then
--             log.info("mb_slave2 从站在线且通信正常")
--         elseif slave2_status == modbus.SLAVE_OFFLINE then
--             log.warn("mb_slave2 从站已离线")
--         elseif slave2_status == modbus.SLAVE_UNKNOWN then
--             log.warn("mb_slave2 从站状态未知")
--         elseif slave2_status == modbus.SLAVE_COMM_TIMEOUT then
--             log.warn("mb_slave2 从站通信超时,可能即将离线")
--         elseif slave2_status == modbus.SLAVE_ERROR then
--             log.error("mb_slave2 从站发生错误")
--         end
--     else
--         log.error("mb_slave2 从站句柄无效")
--     end
-- end, 5000)

-- -- 手动执行通信任务
-- -- 每隔5秒执行一次mb_slave1_msg1消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.create_msg接口中设置为手动模式;成功返回true,其他情况返回false
-- sys.timerLoopStart(function()
--     local status = modbus.exec(mb_ascii, mb_slave1_msg1)
--     log.info("msg", status)
-- end,5000)

-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
-- -- 将在3分钟后删除从站1(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
-- sys.timerStart(function()
--     local status = modbus.remove_slave(mb_ascii, mb_slave1)
--     log.info("modbus", "slave1 remove after 3 minutes")
--     log.info("remove", status)

-- -- 移除从站后,5秒后重新启动Modbus主站
--     sys.timerStart(function()
--         modbus.master_start(mb_ascii)
--         log.info("modbus", "Modbus master restarted after slave removal")
--     end, 5000)
-- end, 180000)

-- -- 删除从站消息
-- -- 第三个参数为空时,则表示删除指定从站的所有消息
-- -- 可在主站运行(master_start 后)或停止状态下调用,均有效
-- modbus.remove_msg(mb_ascii, mb_slave1, mb_slave1_msg1)

-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
sys.timerLoopStart(function()
    -- 检查 mb_slave1 从站状态
    local status = modbus.get_slave_state(mb_slave1)
    -- status 为 0 时表示从站通信正常
    if status == 0 then
        -- 读取缓冲区数据
        -- 重置指针到起始位置
        slave1_msg1_buf:seek(0)

        -- 读取4个寄存器的值(每个寄存器2字节)
        local reg1 = slave1_msg1_buf:readU16()
        local reg2 = slave1_msg1_buf:readU16()
        local reg3 = slave1_msg1_buf:readU16()
        local reg4 = slave1_msg1_buf:readU16()

        -- 创建数据表
        local data = {
            addr = 1,  -- 从站地址
            fun = 3,   -- 功能码03
            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
            reg2 = reg2 / 10,
            reg3 = reg3 / 10,
            reg4 = reg4 / 10,
            timestamp = os.time()  -- 添加时间戳
        }

        -- 转换为JSON
        local json_str = json.encode(data)
        log.info("mb_slave1 Modbus数据转JSON:", json_str)
    else
        log.warn("mb_slave1 从站1状态异常:", status)
    end
end, 1000)

-- -- 将在主站开启2分钟后停止modbus主站
-- sys.timerStart(function()
--     modbus.master_stop(mb_ascii)
--     log.info("modbus", "Modbus stopped after 2 minutes")
-- end, 120000)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus TCP 主站示例程序(文件 1/2:main.lua)

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_master_tcp"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

-- 初始化网络
log.info("ch390", "打开LDO供电")
gpio.setup(140, 1)  --打开开发板lan供电
require "lan"

-- 创建主站设备,Modbus TCP通信模式
-- 设置连接方式为socket.LWIP_ETH
-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH,3000,2000,1,5000)

-- 为主站添加从站,从站ID为1,ip地址为 192.168.0.104,端口号为 6000,最多可添加247个从站
mb_slave1 = modbus.add_slave(mb_tcp, 1, "192.168.4.100", 6000)
-- 为主站添加从站,从站ID为2,ip地址为 192.168.0.104,端口号为 6001
-- mb_slave2 = modbus.add_slave(mb_tcp, 2, "192.168.4.100", 6001)

-- 为从站1创建数据存储区,并创建通讯消息,读取寄存器,默认为自动loop模式
slave1_msg1_buf = zbuff.create(1)
mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
slave1_msg1_buf:clear()

-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
-- slave1_msg1_buf = zbuff.create(1)
-- mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
-- slave1_msg1_buf:clear()

-- -- 为从站2创建数据存储区,并创建通讯消息,写入寄存器
-- slave2_msg1_buf = zbuff.create(1)
-- mb_slave2_msg1 = modbus.create_msg(mb_tcp, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
-- slave2_msg1_buf:clear()

-- 启动Modubs设备
modbus.master_start(mb_tcp)
log.info("start modbus master")

-- -- 设置通讯间隔时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯间隔时间一致,优先级更高
-- -- 设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
-- modbus.set_comm_interval_time(mb_tcp,3000)

-- -- 设置通讯超时时间
-- -- 此处设置的时间与modbus.create_master中设置的通讯超时时间一致,优先级更高
-- -- 当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
-- modbus.set_comm_timeout(mb_tcp, 3000)

-- -- 设置消息发送失败、超时重发次数
-- -- 此处设置的次数与modbus.create_master中设置的消息超时重发次数一致,优先级更高
-- -- 如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
-- modbus.set_comm_resend_count(mb_tcp,1)

-- 设置断线重连时间间隔,若从站与主站断连,主站将在设置时间内重新连接从站
-- modbus.set_comm_reconnection_time(mb_tcp, 5000)

-- -- 设置消息通讯周期
-- -- 仅对modbus.create_msg接口中comm_mode = modbus.LOOP模式的通信任务生效,对 modbus.EXEC 或 modbus.SINGLE 模式通信任务无效(因其不参与周期调度)
-- -- 搭配modbus.create_master/modbus.set_comm_interval_time(mb_tcp, 3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)

-- 获取所有从站状态
-- modbus.get_all_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_tcp then
        local all_slave_status = modbus.get_all_slave_state(mb_tcp)
        log.info("modbus", all_slave_status)
    else
        log.info("modbus", "mb_tcp 主站句柄无效")
    end
end, 5000)

-- 获取从站1的状态
-- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
sys.timerLoopStart(function()
    if mb_slave1 then
        local slave1_status = modbus.get_slave_state(mb_slave1)
        if slave1_status == modbus.SLAVE_NORMAL then
            log.info("mb_slave1 从站在线且通信正常")
        elseif slave1_status == modbus.SLAVE_OFFLINE then
            log.warn("mb_slave1 从站已离线")
        elseif slave1_status == modbus.SLAVE_UNKNOWN then
            log.warn("mb_slave1 从站状态未知")
        elseif slave1_status == modbus.SLAVE_COMM_TIMEOUT then
            log.warn("mb_slave1 从站通信超时,可能即将离线")
        elseif slave1_status == modbus.SLAVE_ERROR then
            log.error("mb_slave1 从站发生错误")
        end
    else
        log.error("mb_slave1 从站句柄无效")
    end
end, 5000)

-- -- 获取从站2的状态
-- -- modbus.get_slave_state接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新)
-- -- 每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
-- sys.timerLoopStart(function()
--     if mb_slave2 then
--         local slave2_status = modbus.get_slave_state(mb_slave2)
--         if slave2_status == modbus.SLAVE_NORMAL then
--             log.info("mb_slave2 从站在线且通信正常")
--         elseif slave2_status == modbus.SLAVE_OFFLINE then
--             log.warn("mb_slave2 从站已离线")
--         elseif slave2_status == modbus.SLAVE_UNKNOWN then
--             log.warn("mb_slave2 从站状态未知")
--         elseif slave2_status == modbus.SLAVE_COMM_TIMEOUT then
--             log.warn("mb_slave2 从站通信超时,可能即将离线")
--         elseif slave2_status == modbus.SLAVE_ERROR then
--             log.error("mb_slave2 从站发生错误")
--         end
--     else
--         log.error("mb_slave2 从站句柄无效")
--     end
-- end, 5000)

-- -- 手动执行通信任务
-- -- 每隔5秒执行一次mb_slave1_msg1消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.create_msg接口中设置为手动模式;成功返回true,其他情况返回false
-- sys.timerLoopStart(function()
--     local status=modbus.exec(mb_tcp, mb_slave1_msg1)
--     log.info("msg",status)
-- end,5000)

-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
-- -- 将在3分钟后删除从站2(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
-- sys.timerStart(function()
--     local status = modbus.remove_slave(mb_tcp, mb_slave2)
--     log.info("modbus", "slave2 remove after 3 minutes")
--     log.info("remove", status)

--     -- 移除从站后,5秒后重新启动Modbus主站
--     sys.timerStart(function()
--         modbus.master_start(mb_tcp)
--         log.info("modbus", "Modbus master restarted after slave removal")
--     end, 5000)
-- end, 180000)

-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
sys.timerLoopStart(function()
    -- 检查从站状态
    local status = modbus.get_slave_state(mb_slave1)
    -- status 为 0 时表示从站通信正常
    if status == 0 then
        -- 读取缓冲区数据
        -- 重置指针到起始位置
        slave1_msg1_buf:seek(0)

        -- 读取4个寄存器的值(每个寄存器2字节)
        local reg1 = slave1_msg1_buf:readU16()
        local reg2 = slave1_msg1_buf:readU16()
        local reg3 = slave1_msg1_buf:readU16()
        local reg4 = slave1_msg1_buf:readU16()

        -- 创建数据表
        local data = {
            addr = 1,  -- 从站地址
            fun = 3,   -- 功能码03
            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
            reg2 = reg2 / 10,
            reg3 = reg3 / 10,
            reg4 = reg4 / 10,
            timestamp = os.time()  -- 添加时间戳
        }

        -- 转换为JSON
        local json_str = json.encode(data)
        log.info("mb_slave1 Modbus数据转JSON:", json_str)

    else
        log.warn("mb_slave1 从站1状态异常:", status)
    end
end, 1000)

-- -- 将在主站开启2分钟后停止modbus主站
-- sys.timerStart(function()
--     modbus.master_stop(mb_tcp)
--     log.info("modbus", "Modbus stopped after 2 minutes")
-- end, 120000)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus TCP 主站示例程序(文件 2/2:lan.lua)

-- 引入必要的库文件(lua编写), 内部库不需要require
dhcps = require "dhcpsrv"
dnsproxy = require "dnsproxy"

sys.taskInit(function ()
    -- sys.wait(3000)
    local result = spi.setup(
        1,-- spi_id
        nil,
        0,-- CPHA
        0,-- CPOL
        8,-- 数据宽度
        51200000--,--频率
        -- spi.MSB,--高低位顺序    可选,默认高位在前
        -- spi.master,--主模式     可选,默认主
        -- spi.full--全双工       可选,默认全双工
    )
    log.info("main", "open", result)
    if result ~= 0 then -- 返回值为 0,表示打开成功
        log.info("main", "spi open error", result)
        return
    end

    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi = 1,cs = 12})
    sys.wait(3000)
    local ipv4, mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
    log.info("ipv4", ipv4, mark, gw)
    while netdrv.link(socket.LWIP_ETH) ~= true do
        sys.wait(100)
    end
    dhcps.create({adapter = socket.LWIP_ETH})

    -- dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
    -- netdrv.napt(socket.LWIP_GP)

    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_ETH)
    netdrv.napt(socket.LWIP_ETH)

end)

sys.taskInit(function()
    -- sys.waitUntil("IP_READY")
    while 1 do
        sys.wait(300000)
        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
        log.info("lua", rtos.meminfo())
        log.info("sys", rtos.meminfo("sys"))
        -- log.info("psram", rtos.meminfo("psram"))
    end
end)

modbus RTU 从站示例程序

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_slave_rtu"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

-- 初始化通讯串口
-- 根据实际设备选取不同的uartid
local uartid = 1
-- 用于控制485接收和发送的使能引脚
local uart485Pin = 17
-- 打开电源(Air8000 开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
gpio.setup(16, 1)
uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)

-- 创建从站设备,Modbus RTU通信模式
local slave_id = 1
mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, slave_id, uartid, 115200)

-- 添加一块寄存器内存区
registers = zbuff.create(1)
modbus.add_block(mb_rtu_s, modbus.REGISTERS, 0, 32, registers)
registers:clear()

-- 创建线圈数据区
ciols = zbuff.create(1)
modbus.add_block(mb_rtu_s, modbus.CIOLS, 0, 32, ciols)
ciols:clear()

-- 注册 Modbus 从站事件回调函数
function modbus_slave_cb(slave_handler, reg_type, opt_type, reg_addr, reg_len)
    -- 用户自定义代码
    -- 打印所有回调参数
    log.info("Modbus 回调:",
        "从站 = ", slave_handler,
        "寄存器类型 = ", reg_type,
        "操作类型 = ", opt_type,
        "起始地址 = ", reg_addr,
        "数量 = ", reg_len
    )
end

-- Modbus 从站注册回调事件接口,modbus_slave_cb 为回调函数
-- modbus.slave_on(mb_rtu_s, modbus_slave_cb)

-- 启动modbus从站
modbus.slave_start(mb_rtu_s)
log.info("start modbus slave")

local counter = 0
-- 修改和读取modbus值
function modify_data()
    counter = counter + 1
    -- 写入寄存器数据 (16位无符号整数)
    registers:seek(0)
    for i = 0,31 do
        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
    end
    -- 写入线圈数据 (1位布尔值)
    ciols:seek(0)
    for i=0,31 do
        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
    end

    -- 读取并打印部分数据用于调试
    registers:seek(0)
    ciols:seek(0)
    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
end

sys.timerLoopStart(modify_data,1000)

-- -- 测试停止modbus从站,将在从站启动两分钟后关闭
-- sys.timerStart(function()
--     modbus.slave_stop(mb_rtu_s)
--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
-- end, 2 * 60 * 1000)  -- 2分钟(单位:毫秒)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus ASCII 从站示例程序

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_slave_ascii"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

-- 初始化通讯串口
-- 根据实际设备选取不同的uartid
local uartid = 1
-- 用于控制485接收和发送的使能引脚
local uart485Pin = 17
-- 打开电源(Air8000 开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
gpio.setup(16, 1)
uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)

-- 创建从站设备,Modbus ASCII模式
local slave_id = 1
mb_ascii_s = modbus.create_slave(modbus.MODBUS_ASCII, slave_id, uartid, 115200)

-- 添加一块寄存器内存区
registers = zbuff.create(1)
modbus.add_block(mb_ascii_s, modbus.REGISTERS, 0, 32, registers)
registers:clear()

-- 创建线圈数据区
ciols = zbuff.create(1)
modbus.add_block(mb_ascii_s, modbus.CIOLS, 0, 32, ciols)
ciols:clear()

-- 注册 Modbus 从站事件回调函数
function modbus_slave_cb(slave_handler, reg_type, opt_type, reg_addr, reg_len)
    -- 用户自定义代码
    -- 打印所有回调参数
    log.info("Modbus 回调:",
        "从站 = ", slave_handler,
        "寄存器类型 = ", reg_type,
        "操作类型 = ", opt_type,
        "起始地址 = ", reg_addr,
        "数量 = ", reg_len
    )
end

-- Modbus 从站注册回调事件接口,modbus_slave_cb 为回调函数
-- modbus.slave_on(mb_ascii_s, modbus_slave_cb)

-- 启动modbus从站
modbus.slave_start(mb_ascii_s)
log.info("start modbus slave")

local counter = 0
-- 修改和读取modbus值
function modify_data()
    counter = counter + 1
    -- 写入寄存器数据 (16位无符号整数)
    registers:seek(0)
    for i=0,31 do
        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
    end  
    -- 写入线圈数据 (1位布尔值)
    ciols:seek(0)
    for i=0,31 do
        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
    end

    -- 读取并打印部分数据用于调试
    registers:seek(0)
    ciols:seek(0)
    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
end

sys.timerLoopStart(modify_data,1000)

-- -- 测试停止modbus从站,将在从站启动两分钟后关闭
-- sys.timerStart(function()
--     modbus.slave_stop(mb_ascii_s)
--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
-- end, 2 * 60 * 1000)  -- 2分钟(单位:毫秒)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus TCP 从站示例程序(文件 1/2:main.lua)

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "modbus_slave_tcp"
VERSION = "001.000.000"

log.info("main", PROJECT, VERSION)

-- 开启调试模式
modbus.debug(1)
-- -- 关闭调试模式
-- modbus.debug(0)

log.info("ch390", "打开LDO供电")
gpio.setup(140, 1)  --打开开发板lan供电
require "lan"

-- 创建从站设备,Modbus TCP通信模式;
-- 设置该从站端口号为6000,网卡适配器序列号为socket.LWIP_ETH。
local slave_id = 1
mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, slave_id, 6000, socket.LWIP_ETH)

-- 创建寄存器数据区
registers = zbuff.create(1)
modbus.add_block(mb_tcp_s, modbus.REGISTERS, 0, 32, registers)
registers:clear()

-- 创建线圈数据区
ciols = zbuff.create(1)
modbus.add_block(mb_tcp_s, modbus.CIOLS, 0, 32, ciols)
ciols:clear()

-- 注册 Modbus 从站事件回调函数
function modbus_slave_cb(slave_handler, reg_type, opt_type, reg_addr, reg_len)
    -- 用户自定义代码
    -- 打印所有回调参数
    log.info("Modbus 回调:",
        "从站 = ", slave_handler,
        "寄存器类型 = ", reg_type,
        "操作类型 = ", opt_type,
        "起始地址 = ", reg_addr,
        "数量 = ", reg_len
    )
end

-- Modbus 从站注册回调事件接口,modbus_slave_cb 为回调函数
-- modbus.slave_on(mb_tcp_s, modbus_slave_cb)

-- 启动modbus从站
modbus.slave_start(mb_tcp_s)
log.info("start modbus slave")

local counter = 0
-- 修改和读取modbus值
function modify_data()
    counter = counter + 1
    -- 写入寄存器数据 (16位无符号整数)
    registers:seek(0)
    for i=0,31 do
        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
    end
    -- 写入线圈数据 (1位布尔值)
    ciols:seek(0)
    for i=0,31 do
        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
    end

    -- 读取并打印部分数据用于调试
    registers:seek(0)
    ciols:seek(0)
    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
end
sys.timerLoopStart(modify_data,1000)

-- -- 测试停止modbus从站,从站将在开启两分钟后关闭
-- sys.timerStart(function()
--     modbus.slave_stop(mb_rtu_s)
--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
-- end, 120000)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

modbus TCP 从站示例程序(文件 2/2:lan.lua)

-- 引入必要的库文件(lua编写), 内部库不需要require
dhcps = require "dhcpsrv"
dnsproxy = require "dnsproxy"

sys.taskInit(function ()
    -- sys.wait(3000)
    local result = spi.setup(
        1,-- spi_id
        nil,
        0,-- CPHA
        0,-- CPOL
        8,-- 数据宽度
        51200000--,--频率
        -- spi.MSB,--高低位顺序    可选,默认高位在前
        -- spi.master,--主模式     可选,默认主
        -- spi.full--全双工       可选,默认全双工
    )
    log.info("main", "open", result)
    if result ~= 0 then -- 返回值为 0,表示打开成功
        log.info("main", "spi open error", result)
        return
    end

    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi = 1,cs = 12})
    sys.wait(3000)
    local ipv4, mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
    log.info("ipv4", ipv4, mark, gw)
    while netdrv.link(socket.LWIP_ETH) ~= true do
        sys.wait(100)
    end
    dhcps.create({adapter = socket.LWIP_ETH})

    -- dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
    -- netdrv.napt(socket.LWIP_GP)

    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_ETH)
    netdrv.napt(socket.LWIP_ETH)

end)

sys.taskInit(function()
    -- sys.waitUntil("IP_READY")
    while 1 do
        sys.wait(300000)
        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
        log.info("lua", rtos.meminfo())
        log.info("sys", rtos.meminfo("sys"))
        -- log.info("psram", rtos.meminfo("psram"))
    end
end)

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;

如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;

3.1 modbus.MODBUS_RTU

常量含义:表示指定主站与从站之间采用 Modbus RTU 通信模式;
数据类型:number
常量取值:0
示例代码:-- 创建一个 Modbus Master 主站句柄;
         -- modbus.MODBUS_RTU 表示指定主/从站之间采用 Modbus RTU 通信模式;
         -- modbus.create_master 接口其他参数本文也有说明,搜索接口名称即可;
         mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)

         -- 创建一个 Modbus slave 从站句柄;
         -- modbus.MODBUS_RTU 表示指定主/从站之间采用 Modbus RTU 通信模式;
         -- modbus.create_slave 接口其他参数本文也有说明,搜索接口名称即可;
         mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, 1, 1, 9600)

3.2 modbus.MODBUS_ASCII

常量含义:表示指定主站与从站之间采用 Modbus ASCII 通信模式;
数据类型:number
常量取值:1
示例代码:-- 创建一个 Modbus Master 主站句柄;
         -- modbus.MODBUS_ASCII 表示指定主/从站之间采用 Modbus ASCII 通信模式;
         -- modbus.create_master 接口其他参数本文也有说明,搜索接口名称即可;
         mb_ascii = modbus.create_master(modbus.MODBUS_ASCII, uartid, 9600)

         -- 创建一个 Modbus slave 从站句柄;
         -- modbus.MODBUS_ASCII 表示指定主/从站之间采用 Modbus ASCII 通信模式;
         -- modbus.create_slave 接口其他参数本文也有说明,搜索接口名称即可;
         mb_ascii_s = modbus.create_slave(modbus.MODBUS_ASCII, 1, 1, 9600)

3.3 modbus.MODBUS_TCP

常量含义:表示指定主站与从站之间采用 Modbus TCP 通信模式;
数据类型:number
常量取值:2
示例代码:-- 创建一个 Modbus Master 主站句柄;
         -- modbus.MODBUS_TCP 表示指定主/从站之间采用 Modbus TCP 通信模式;
         -- modbus.create_master 接口其他参数本文也有说明,搜索接口名称即可;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)

         -- 创建一个 Modbus slave 从站句柄;
         -- modbus.MODBUS_TCP 表示指定主/从站之间采用 Modbus TCP 通信模式;
         -- modbus.create_slave 接口其他参数本文也有说明,搜索接口名称即可;
         mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502, socket.LWIP_ETH)

3.4 modbus.COIL_STATUS

常量含义:表示主站访问从站时操作的寄存器类型为线圈;
         兼容老变量名 modbus.CIOLS
数据类型:number
常量取值:0
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.COIL_STATUS 表示主站访问从站时操作的寄存器类型为线圈;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.COIL_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.LOOP)

         -- 添加一块寄存器内存区;
         -- modbus.COIL_STATUS 表示这块内存区的寄存器类型为线圈;
         -- modbus.add_block 接口其他参数本文也有说明,搜索接口名称即可;
         modbus.add_block(mb_tcp_s, modbus.COIL_STATUS, 0, 32, registers)

3.5 modbus.INPUT_STATUS

常量含义:表示主站访问从站时操作的寄存器类型为离散输入;
         兼容老变量名 modbus.INPUTS
数据类型:number
常量取值:1
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.INPUT_STATUS 表示主站访问从站时操作的寄存器类型为离散输入;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.INPUT_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.LOOP)

         -- 添加一块寄存器内存区;
         -- modbus.INPUT_STATUS 表示这块内存区的寄存器类型为离散输入;
         -- modbus.add_block 接口其他参数本文也有说明,搜索接口名称即可;
         modbus.add_block(mb_tcp_s, modbus.INPUT_STATUS, 0, 32, registers)

3.6 modbus.INPUT_REGISTER

常量含义:表示主站访问从站时操作的寄存器类型为输入寄存器;
         兼容老变量名 modbus.INPUT_REGISTERS
数据类型:number
常量取值:3
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.INPUT_REGISTER 表示主站访问从站时操作的寄存器类型为输入寄存器;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.INPUT_REGISTER, modbus.READ, 0, 10, zbuf, 1, modbus.LOOP)

         -- 添加一块寄存器内存区;
         -- modbus.INPUT_REGISTER 表示这块内存区的寄存器类型为输入寄存器;
         -- modbus.add_block 接口其他参数本文也有说明,搜索接口名称即可;
         modbus.add_block(mb_tcp_s, modbus.INPUT_REGISTER, 0, 32, registers)

3.7 modbus.HOLDING_REGISTER

常量含义:表示主站访问从站时操作的寄存器类型为保持寄存器;
         兼容老变量名 modbus.REGISTERS
数据类型:number
常量取值:4
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.HOLDING_REGISTER 表示主站访问从站时操作的寄存器类型为保持寄存器;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.READ, 0, 10, zbuf, 1, modbus.LOOP)

         -- 添加一块寄存器内存区;
         -- modbus.HOLDING_REGISTER 表示这块内存区的寄存器类型为保持寄存器;
         -- modbus.add_block 接口其他参数本文也有说明,搜索接口名称即可;
         modbus.add_block(mb_tcp_s, modbus.HOLDING_REGISTER, 0, 32, registers)

3.8 modbus.READ

常量含义:表示主站对从站执行读取操作,具体读单个或多个寄存器由寄存器数量参数决定;
数据类型:number
常量取值:0
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.READ 表示主站对从站执行读取操作,具体读单个或多个寄存器由寄存器数量参数决定;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.READ, 0, 10, zbuf, 1, modbus.LOOP)

3.9 modbus.WRITE

常量含义:表示主站对从站执行写入操作,具体写单个或多个寄存器由寄存器数量参数决定;
数据类型:number
常量取值:1
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.WRITE 表示主站对从站执行写入操作,具体写单个或多个寄存器由寄存器数量参数决定;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.WRITE, 0, 10, zbuf, 1, modbus.LOOP)

3.10 modbus.WRITE_SINGLE

常量含义:表示主站对从站执行单个寄存器的写入操作,寄存器数量参数总是为 1
数据类型:number
常量取值:2
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.WRITE_SINGLE 表示主站对从站执行单个寄存器的写入操作,寄存器数量参数总是为 1;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.WRITE_SINGLE, 0, 1, zbuf, 1, modbus.LOOP)

3.11 modbus.LOOP

常量含义:表示指定通信任务以自动循环方式执行,系统将按周期重复发起请求;
数据类型:number
常量取值:0
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.LOOP 表示指定通信任务以自动循环方式执行,系统将按周期重复发起请求;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.WRITE_SINGLE, 0, 1, zbuf, 1, modbus.LOOP)

3.12 modbus.EXEC

常量含义:表示指定通信任务为手动执行模式,需调用 modbus.exec 接口显式触发通信;
数据类型:number
常量取值:2
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.EXEC 表示指定通信任务为手动执行模式,需调用执行接口显式触发通信;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.WRITE_SINGLE, 0, 1, zbuf, 1, modbus.EXEC)

3.13 modbus.SINGLE

常量含义:表示指定通信任务仅自动执行一次,通信成功后自动释放句柄;
数据类型:number
常量取值:3
示例代码:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         -- modbus.SINGLE 表示指定通信任务仅自动执行一次,通信成功后自动释放句柄;
         -- modbus.create_msg 接口其他参数本文也有说明,搜索接口名称即可;
         msg = modbus.create_msg(mb_rtu, slave, modbus.HOLDING_REGISTER, modbus.WRITE_SINGLE, 0, 1, zbuf, 1, modbus.SINGLE)

3.14 modbus.SLAVE_NORMAL

常量含义:表示从站状态正常;
数据类型:number
常量取值:0
示例代码:-- 获取一个从站的状态;
         -- modbus.SLAVE_NORMAL 表示从站状态正常;
         if modbus.SLAVE_NORMAL == modbus.get_slave_state(slave) then
             -- 从站 slave 状态正常;
             log.info("modbus.get_slave_state:", "从站 slave 状态正常")
         end

3.15 modbus.SLAVE_OFFLINE

常量含义:表示从站状态离线;
数据类型:number
常量取值:1
示例代码:-- 获取一个从站的状态;
         -- modbus.SLAVE_OFFLINE 表示从站状态离线;
         if modbus.SLAVE_OFFLINE == modbus.get_slave_state(slave) then
             -- 从站 slave 状态正常;
             log.info("modbus.get_slave_state:", "从站 slave 状态离线")
         end

3.16 modbus.SLAVE_UNKNOWN

常量含义:表示从站状态未知;
数据类型:number
常量取值:2
示例代码:-- 获取一个从站的状态;
         -- modbus.SLAVE_UNKNOWN 表示从站状态未知;
         if modbus.SLAVE_UNKNOWN == modbus.get_slave_state(slave) then
             -- 从站 slave 状态未知;
             log.info("modbus.get_slave_state:", "从站 slave 状态未知")
         end

3.17 modbus.SLAVE_COMM_TIMEOUT

常量含义:表示主站与从站之间的通信超时,超时多次后从站状态转为离线;
数据类型:number
常量取值:3
示例代码:-- 获取一个从站的状态;
         -- modbus.SLAVE_COMM_TIMEOUT 表示主站与从站之间的通信超时,超时多次后从站状态转为离线;
         if modbus.SLAVE_COMM_TIMEOUT == modbus.get_slave_state(slave) then
             -- 主站与从站 slave 之间的通信超时;
             log.info("modbus.get_slave_state:", "主站与从站 slave 之间的通信超时")
         end

3.18 modbus.SLAVE_ERROR

常量含义:表示从站错误;
数据类型:number
常量取值:4
示例代码:-- 获取一个从站的状态;
         -- modbus.SLAVE_ERROR 表示从站错误;
         if modbus.SLAVE_ERROR == modbus.get_slave_state(slave) then
             -- 从站 slave 错误;
             log.info("modbus.get_slave_state:", "从站 slave 错误")
         end

四、函数详解

4.1 modbus.create_master(type, drive_id, baud_rate, comm_interval_time, comm_timeout, comm_resend_count, comm_reconnection_time)

功能

表示创建一个 Modbus Master 主站句柄;

注意事项

1. 使用该接口之前需要初始化好对应的 UART 参数或者网络;

2. 需要根据 modbus RTU/ASCII 与 modbus TCP 通信模式选用对应的参数;

3. 创建失败时返回值为 nil ,建议检查返回值,避免对无效句柄进行后续操作;

4. comm_reconnection_time 参数对 modbus RTU/ASCII 无效,串口不存在“连接断开”、“自动重连”概念;

参数

type

参数含义:表示指定 Modbus 主站使用的通信模式类型;
数据类型:number
取值范围:modbus.MODBUS_RTU  modbus.MODBUS_ASCII  modbus.MODBUS_TCP
是否必选:必须传入此参数;
注意事项:若传入无效值,函数将返回 nil
参数示例:-- 指定主/从站之间采用 Modbus RTU 通信模式;
         type = modbus.MODBUS_RTU

drive_id

参数含义:表示通信驱动标识符;
         若为 Modbus RTU/ASCII 模式,表示 UART 端口号(uartid);
         若为 Modbus TCP 模式,表示 网络适配器索引(adapter_index);
数据类型:number
取值范围:UART ID:平台支持的串口编号,如 0/1/2,具体看硬件;
         网卡索引:如 socket.LWIP_ETH,参考 socket 核心库;
是否必选:必须传入此参数;
注意事项:必须确保该驱动已提前初始化(如调用 uart.setup 或者网络已连接);
参数示例:-- Modbus RTU/ASCII 模式;
         uartid = 1
         drive_id = uartid

         -- Modbus TCP 模式;
         drive_id = socket.LWIP_ETH

baud_rate

参数含义:表示 Modbus RTU/ASCII 模式时,串口通信的波特率;
数据类型:number
取值范围:常见值为 1200240048009600192003840057600115200
         由于是做 Modbus RTU/ASCII 串口通信使用,建议使用 9600 或者 115200
是否必选:必须传入此参数;
注意事项:仅对 Modbus RTU/ASCII 模式有效;
         Modbus TCP 模式时,modbus.create_master 接口的这个位置的参数是 comm_interval_time
参数示例:-- 配置串口通信的波特率为 9600;
         -- 此参数仅对 Modbus RTU/ASCII 模式有效;
         baud_rate = 9600

comm_interval_time

参数含义:表示主站任务调度时的通信间隔时间(单位:毫秒);
数据类型:number
取值范围:10 ~ 65535
是否必选:可选传入此参数(默认为 100 毫秒);
注意事项:在 Modbus RTU/ASCII 模式下,这是第 4 个参数;
          Modbus TCP 模式下,这是第 3 个参数(因为 Modbus TCP 跳过了波特率);
参数示例:-- 配置主站每 1000 毫秒调度一次通信任务;
         comm_interval_time = 100

comm_timeout

参数含义:表示主站向从站发送单次 Modbus 请求的超时时间(单位:毫秒);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 1000 毫秒);
注意事项:从主站发送请求到等待从站响应的最大时间;
参数示例:-- 配置主站向从站发送单次 Modbus 请求的超时时间为 1 秒(1000 毫秒),即等待 1 秒无响应则为超时;
         comm_timeout = 1000

comm_resend_count

参数含义:表示主站向从站通信失败(超时或错误)后的最大重发次数;
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 1 次);
注意事项:总尝试次数 = 1 + comm_resend_count
         设置为 0 表示不重试,失败即结束;
参数示例:-- 配置主站向从站通信失败(超时或错误)后的最大重发次数为 1;
         -- 则总尝试次数为 2(1 次重发);
         comm_resend_count = 1

comm_reconnection_time

参数含义:表示 Modbus TCP 模式时,TCP 连接断开后的自动重连时间间隔(单位:毫秒);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 5000 毫秒);
注意事项:该参数仅对 Modbus TCP 模式有效;
参数示例:-- 配置 Modbus TCP 模式时,TCP 连接断开后的自动重连时间间隔为 5 秒(5000 毫秒);
         comm_reconnection_time = 5000

返回值

local mb_xxx = modbus.create_master(type, drive_id, baud_rate, comm_interval_time, comm_timeout, comm_resend_count, comm_reconnection_time)

有一个返回值为 mb_xxx;

mb_xxx

含义说明:创建 Modbus 主站(Master)成功时返回其句柄(上下文对象),用于后续通信操作;
         创建失败时返回 nil
数值类型:userdata  nil
取值范围:无特别限制;
注意事项:必须显式检查返回值是否为 nil,避免对无效句柄进行后续操作(如创建消息、读写寄存器),否则将导致 Lua 运行时错误;
返回示例:创建成功时为一个 userdata 类型的对象;
         创建失败时为 nil

示例

-- 注意: 用户在调用该接口之前,需要自行初始化对应的硬件端口;Modbus RTU/ASCII 模式需初始化 uart,Modbus TCP 模式需初始化网络;

-- 示例1:创建 Modbus RTU 主站(串口);
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)

-- 示例2:创建 Modbus TCP 主站(以太网);
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH, 100, 1000, 1, 5000)

4.2 modbus.set_comm_interval_time(master_handler, time_ms)

功能

表示设置指定 Modbus 主站的通信任务调度间隔时间,控制主站轮询任务的最小执行频率;

注意事项

1. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

2. 该间隔时间影响所有使用此主站的通信任务(如通过 modbus.create_msg 接口创建的任务)的调度频率;

3. 此设置对 modbus.EXEC 模式的手动任务无影响(因其不参与自动调度);

参数

master_handler

参数含义:表示通过 modbus.create_master 接口创建的 Modbus 主站句柄,用于标识要设置通信间隔的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus RTU 主站;
         mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
         -- 将创建的 Modbus RTU 主站句柄传给 master_handler;
         master_handler = mb_rtu

time_ms

参数含义:表示 Modbus 主站的通信任务调度间隔时间(单位:毫秒);
         主站将以此为最小周期调度所有自动执行的通信任务(如 LOOP 模式任务);
数据类型:number
取值范围:10 ~ 65535
是否必选:可选传入此参数(默认为 100 毫秒);
注意事项:该设置对 modbus.EXEC(手动触发)模式的任务无效;
参数示例:-- 配置 Modbus 主站的通信任务调度间隔时间为 100 毫秒;
         time_ms = 100

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
if mb_rtu == nil then
    log.info("主站创建失败")
    return
end

-- 设置通信间隔时间为 200 毫秒;
modbus.set_comm_interval_time(mb_rtu, 200)

4.3 modbus.set_comm_timeout(master_handler, time_ms)

功能

表示设置指定 Modbus 主站的通信超时时间,定义主站发送请求后等待从站响应的最长时间(单位:毫秒);超时后若配置了重试机制,将触发重发;否则判定本次通信失败;

注意事项

1. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

2. 该超时时间适用于该主站下所有通信任务(包括 LOOP、SINGLE 和 EXEC 模式);

参数

master_handler

参数含义:表示通过 modbus.create_master 接口创建的 Modbus 主站句柄,用于指定要设置超时时间的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus RTU 主站;
         mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
         -- 将创建的 Modbus RTU 主站句柄传给 master_handler;
         master_handler = mb_rtu

time_ms

参数含义:表示单次 Modbus 通信的超时时间,即主站发送请求后等待从站响应的最长时间(单位:毫秒);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 3000 毫秒);
注意事项:超时时间应大于从站的最大响应延迟,否则会误判为通信失败;
参数示例:-- 配置单次 Modbus 通信时的超时时间为 1 秒(1000 毫秒);
         time_ms = 1000

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
if mb_rtu == nil then
    log.info("主站创建失败")
    return
end

-- 设置通信超时时间为 2 秒(2000 毫秒);
modbus.set_comm_timeout(mb_rtu, 2000)

4.4 modbus.set_comm_resend_count(master_handler, resend_count)

功能

表示设置指定 Modbus 主站的消息重发次数;

当通信发生超时或失败时,主站将自动重试发送请求,最大重试次数由 resend_count 参数控制;

实际总尝试次数 = 1(首次)+ resend_count;

注意事项

1. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

2. 该设置对主站下所有通信任务(包括 LOOP、SINGLE、EXEC 模式)均生效;

3. 若 resend_count = 0,则失败后不重试,直接返回错误;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要配置重发次数的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus RTU 主站;
         mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
         -- 将创建的 Modbus RTU 主站句柄传给 master_handler;
         master_handler = mb_rtu

resend_count

参数含义:表示消息发送失败或超时后的最大重发次数;
         主站在首次发送失败后,最多再尝试发送 resend_count 次;
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 1 次);
注意事项:resend_count = 0 时,表示失败后不重试;
参数示例:-- 配置消息发送失败或超时后的最大重发次数为 0 次,即通信失败后不重试;
         resend_count = 0

         -- 配置消息发送失败或超时后的最大重发次数为 1 次,总尝试次数为 2 次;
         resend_count = 1

         -- 配置消息发送失败或超时后的最大重发次数为 2 次,总尝试次数为 3 次;
         resend_count = 2

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
if mb_rtu == nil then
    log.info("主站创建失败")
    return
end

-- 设置失败后最多重发 2 次(共尝试 3 次);
modbus.set_comm_resend_count(mb_rtu, 2)

4.5 modbus.set_comm_reconnection_time(master_handler, reconnection_time)

功能

表示设置指定 Modbus 主站的断线重连时间间隔(单位:毫秒);

当主站检测到 TCP 连接断开(如网络中断、从站宕机)时,将按此间隔周期性尝试重新建立连接;

该功能仅对 Modbus TCP 模式主站生效,对 Modbus RTU/ASCII 模式主站无效;

注意事项

1. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

2. 仅适用于 modbus.MODBUS_TCP 模式主站生效,对 Modbus RTU/ASCII 模式主站无效;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要配置重连间隔的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

reconnection_time

参数含义:表示 TCP 连接断开后,主站自动尝试重新连接的时间间隔(单位:毫秒);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 3000 毫秒);
注意事项:仅对 modbus.MODBUS_TCP 模式主站有效,Modbus RTU/ASCII 模式主站下该参数被忽略;
参数示例:-- 配置 TCP 连接断开后,自动重连的时间间隔为 3 秒(3000 毫秒);
         reconnection_time = 3000

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus TCP 主站;
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
if mb_tcp == nil then
    log.info("主站创建失败")
    return
end

-- 设置断线后每 5 秒尝试重连一次
modbus.set_comm_reconnection_time(mb_tcp, 5000)

4.6 modbus.get_all_slave_state(master_handler)

功能

表示检查指定 Modbus 主站下所有已注册从站的当前通信状态;

若所有从站均处于“正常”状态(如最近通信成功、无超时、连接有效等),则返回 true;

只要任意一个从站异常(如超时、无响应、TCP 断连等),即返回 false;

注意事项

1. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

2. 该接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新);

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要查询从站状态的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

返回值

local all_ok = modbus.get_all_slave_state(master_handler)

有一个返回值为 all_ok;

all_ok

含义说明:表示检查指定 Modbus 主站下所有已注册从站的当前通信状态;
         若所有从站均处于“正常”状态(如最近通信成功、无超时、连接有效等),则返回 true
         只要任意一个从站异常(如超时、无响应、TCP 断连等),即返回 false
数值类型:boolean
取值范围:true  false
注意事项:无;
返回示例:所有从站均处于“正常”状态时 all_ok  true
         有任意一个从站处于异常状态时 all_ok  false

示例

-- 创建 Modbus TCP 主站;
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
if mb_tcp == nil then
    log.info("主站创建失败")
    return
end

-- 检查所有从站是否正常
local all_ok = modbus.get_all_slave_state(mb_tcp)
if all_ok then
    log.info("所有从站状态正常")
else
    log.info("部分从站异常或无响应")
end

4.7 modbus.exec(master_handler, msg_handler)

功能

表示将一条已创建的 Modbus 通信任务(必须为 modbus.EXEC 模式)提交给主站,以最高优先级插入当前通信队列(在当前的请求周期完成后执行);

执行完成后任务自动结束,不会重复执行;

注意事项

1. msg_handler 参数必须是在调用 modbus.create_msg 接口创建时指定 comm_mode = modbus.EXEC 的任务,否则执行无效(通常返回 false);

2. 该接口不会中断当前正在进行的 Modbus 通信,而是等当前周期完成后执行;

3. 返回值仅反映本次接口调用情况;

4. 若需确认从站状态,可调用 modbus.get_slave_state 接口进行操作;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要执行指令的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

msg_handler

参数含义:表示通过 modbus.create_msg 创建的通信任务句柄,用于定义具体操作内容;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_msg 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         msg = modbus.create_msg(mb_tcp, slave, modbus.COIL_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.EXEC)
         -- 将创建的 msg 通信任务句柄传给 msg_handler;
         msg_handler = msg

返回值

local success = modbus.exec(master_handler, msg_handler)

有一个返回值 success;

success

含义说明:表示这个接口的调用情况;
数值类型:boolean
取值范围:true  false
注意事项:这个返回值仅反映本次接口调用情况;
         若需确认从站状态,可调用 modbus.get_slave_state 接口进行操作;
返回示例:接口调用成功时 success  true
         接口调用失败时 success  false

示例

-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
msg = modbus.create_msg(mb_tcp, slave, modbus.COIL_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.EXEC)

-- 将 msg 通信任务以最高优先级插入当前通信队列(在当前请求周期完成后执行);
local ok = modbus.exec(mb_tcp, msg)

-- 注意:ok == true 仅表示接口调用成功;
if ok then
    log.info("modbus.exec 接口调用成功")
else
    log.info("modbus.exec 接口调用失败")
end

4.8 modbus.master_start(master_handler)

功能

表示完成 Modbus 主站的初始化并激活其通信能力;

调用此接口成功后,主站才真正进入“可工作”状态,所有后续的通信操作(包括自动任务和手动任务)才能被正常处理;

未调用此接口前,即使创建了任务,主站也不会执行任何通信;

注意事项

1. 必须在 modbus.create_master 之后、任何通信操作(如 modbus.exec 或自动任务调度)之前调用;

2. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要激活的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus TCP 主站;
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
if mb_tcp == nil then
    log.info("主站创建失败")
    return
end

-- 启动 Modbus 主站
modbus.master_start(mb_tcp)

4.9 modbus.master_stop(master_handler)

功能

表示停止指定 Modbus 主站的协议栈运行,使其退出工作状态;

调用后,主站将立即停止处理所有通信任务(自动调度任务 + 手动触发任务);

重新启动后会按照之前配置的参数继续执行;

注意事项

1. 调用后,主站进入“停止”状态,不再响应任何通信请求,即使任务已创建或正在排队;

2. 调用后,会等当前周期执行完成后才会停止;

3. 必须在 modbus.master_start 成功调用后才能调用此接口,否则无意义;

4. 停止后,主站句柄仍有效,可再次调用 modbus.master_start 重新启用;

5. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定要停止的目标主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建 Modbus TCP 主站;
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
if mb_tcp == nil then
    log.info("主站创建失败")
    return
end

-- 停止 Modbus 主站
modbus.master_stop(mb_tcp)

4.10 modbus.add_slave(master_handler, slave_id, ip, port)

功能

表示向指定的 Modbus 主站创建并注册一个从站(Slave)对象;

该从站将作为后续通信任务(如 modbus.create_msg)的目标设备;

注意事项

1. 必须在创建通信任务前完成;

2. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定从站归属的主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

slave_id

参数含义:表示 Modbus 从站地址(也称站号),用于在通信帧中标识目标设备;
数据类型:number
取值范围:1 ~ 247
是否必选:必须传入此参数;
注意事项:无;
参数示例:-- 设置从站地址为 1;
         slave_id = 1

ip

参数含义:表示 Modbus TCP 通信模式下从站的 IPv4 地址字符串;
数据类型:string
取值范围:标准 IPv4 地址格式,如 "192.168.1.100""10.0.0.50"
是否必选:仅 Modbus TCP 通信模式时必须传入此参数;
         Modbus RTU/ASCII 通信模式时忽略(可不传);
注意事项:Modbus RTU/ASCII 模式下调用时,此参数不会被使用,也不会报错;
         IP 地址必须可达,否则后续通信会超时;
参数示例:-- 主站为 Modbus TCP 通信模式时,设置创建的从站设备 IP 地址为 "192.168.10.133";
         ip = "192.168.10.133"

port

参数含义:表示 Modbus TCP 通信模式下从站的监听端口号;
数据类型:number
取值范围:1 ~ 65535Modbus TCP 标准端口为 502);
是否必选:仅 Modbus TCP 通信模式时必须传入此参数;
         Modbus RTU/ASCII 通信模式时忽略(可不传);
注意事项:Modbus RTU/ASCII 模式下调用时,此参数不会被使用,也不会报错;
         端口不可用或防火墙阻挡会导致后续连接失败;
参数示例:-- 主站为 Modbus TCP 通信模式时,设置创建的从站设备的监听端口号为 502;
         port = 502

返回值

local slave = modbus.add_slave(master_handler, slave_id, ip, port)

有一个返回值 slave;

slave

含义说明:表示向主站创建并添加 Modbus 从站时的情况;
数值类型:userdata  nil
取值范围:无特别限制;
注意事项:必须显式检查返回值是否为 nil,避免对无效对象进行后续操作;
返回示例:创建成功时为一个 userdata 类型的对象;
         创建失败时为 nil

示例

-- 示例 1:创建一个 Modbus RTU 主站(忽略 IP 和端口);
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus RTU 从站;
slave1 = modbus.add_slave(mb_rtu, 1)
if not slave1 then
    log.info("Modbus RTU 从站创建失败")
    return
end

-- 示例 2:创建一个 Modbus TCP 主站(需指定 IP 和端口)
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
slave2 = modbus.add_slave(mb_tcp, 1, "192.168.10.133", 502)
if not slave2 then
    log.info("Modbus TCP 从站创建失败")
    return
end

4.11 modbus.remove_slave(master_handler, slave_handler)

功能

表示从指定 Modbus 主站中删除一个已注册的从站对象,并自动清理所有与该从站关联的通信任务(消息句柄);

此操作用于释放从站占用的内存和资源,防止内存泄漏;

注意事项

1. 必须在主站已停止(modbus.master_stop 已调用)的状态下执行,否则操作无效(返回 false);

2. 删除后,所有通过 modbus.create_msg 创建的、关联该从站的消息句柄将自动失效,不应再使用;

3. master_handler 参数必须是通过 modbus.create_master 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定删除从站的主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
         主站必须处于已停止状态(即已调用 modbus.master_stop),否则删除无效;
         必须与 slave_handler 所属主站一致,否则返回 false
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 停止 mb_tcp 主站;
         modbus.master_stop(mb_tcp)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

slave_handler

参数含义:表示通过 modbus.add_slave 创建的从站句柄,用于指定删除的具体从站对象;
数据类型:userdata  nil
取值范围:必须是由 modbus.add_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站必须属于 master_handler 所指向的主站;
         删除后,该句柄不应再用于任何操作(如创建新消息);
         若从站已被删除或从未注册,返回 false
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 向 mb_tcp 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
         slave = modbus.add_slave(mb_tcp, 1)
         -- 停止 mb_tcp 主站;
         modbus.master_stop(mb_tcp)
         -- 将创建的 slave 从站句柄传给 slave_handler;
         slave_handler = slave

返回值

local remove_slave_state = mqttc:subscribe(topic, qos)

有一个返回值 remove_slave_state;

remove_slave_state

含义说明:表示单个从站及其关联消息的删除情况;
数值类型:boolean
取值范围:true  false
注意事项:无;
返回示例:从站及其关联消息成功被删除时 remove_slave_state  true
         删除失败时 remove_slave_state  false

示例

-- 创建一个 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)
-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus RTU 从站;
slave = modbus.add_slave(mb_rtu, 1)

-- 先停止 mb_rtu 主站;
modbus.master_stop(mb_rtu)

-- 再删除 slave 从站
local remove_slave_state = modbus.remove_slave(mb, slave)
if remove_slave_state then
    log.info("slave 从站删除成功")
else
    log.info("slave 从站删除失败")
end

4.12 modbus.get_slave_state(slave_handler)

功能

表示获取指定 Modbus 从站的当前运行状态,用于判断从站是否在线等情况;

注意事项

1. 该接口不主动发起通信,仅查询主站内部维护的从站状态(通常由最近一次通信结果或连接状态更新);

2. slave_handler 参数必须是通过 modbus.add_slave 接口成功创建的有效句柄,传入 nil 或无效句柄将导致 Lua 运行错误;

3. 若主站从来没有开启过,返回的是 modbus.SLAVE_UNKNOWN(状态未知);如果主站开启过又停止了,返回的是停止前的状态;

参数

slave_handler

参数含义:表示通过 modbus.add_slave 创建的从站句柄,用于指定要查询状态的目标从站;
数据类型:userdata  nil
取值范围:必须是由 modbus.add_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站必须已成功添加到主站;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 向 mb_tcp 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
         slave = modbus.add_slave(mb_tcp, 1)
         -- 将创建的 slave 从站句柄传给 slave_handler;
         slave_handler = slave

返回值

local slave_state = modbus.get_slave_state(slave_handler)

有一个返回值 slave_state;

slave_state

含义说明:表示某个从站的状态情况;
数值类型:number
取值范围:返回值为 0,对应常量 modbus.SLAVE_NORMAL(状态正常);
         返回值为 1,对应常量 modbus.SLAVE_OFFLINE(设备离线);
         返回值为 2,对应常量 modbus.SLAVE_UNKNOWN(状态未知);
         返回值为 3,对应常量 modbus.SLAVE_COMM_TIMEOUT(通讯超时,超时N次转化为离线);
         返回值为 4,对应常量 modbus.SLAVE_ERROR(错误);
注意事项:无;
返回示例:该从站状态正常时,slave_state 的值为 0
         该从站状态离线时,slave_state 的值为 1
         该从站状态未知时,slave_state 的值为 2
         该从站状态通讯超时时,slave_state 的值为 3
         该从站状态错误时,slave_state 的值为 4

示例

-- 创建 Modbus TCP 主站;
mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
-- 向 mb_tcp 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
slave = modbus.add_slave(mb_tcp, 1)

-- 获取从站状态
local slave_state = modbus.get_slave_state(slave)

if slave_state == modbus.SLAVE_NORMAL then
    log.info("从站在线且通信正常")
elseif slave_state == modbus.SLAVE_OFFLINE then
    log.warn("从站已离线")
elseif slave_state == modbus.SLAVE_UNKNOWN then
    log.warn("从站状态未知")
elseif slave_state == modbus.SLAVE_COMM_TIMEOUT then
    log.warn("从站通信超时,可能即将离线")
elseif slave_state == modbus.SLAVE_ERROR then
    log.info("错误")
end

4.13 modbus.create_msg(master_handler, slave_handler, reg_type, opt_type, reg_addr, reg_len, data_addr, comm_period,comm_mode)

功能

表示向指定 Modbus 从站创建并注册一条通信任务(消息),定义后续要执行的读/写操作(包括寄存器类型、地址、数据缓冲区、执行模式等);

该任务将根据 comm_mode 配置,在主站启动后自动或手动执行;

注意事项

1. 在 modbus.master_start 之前或之后调用均可,但任务仅在主站启动后才会被执行;

2. data_addr 必须是通过 zbuff.create 创建的有效缓冲区句柄,用于存放读取数据或提供写入数据;

3. 若 opt_type = modbus.WRITE_SINGLE,则 reg_len 必须为 1(接口内部会强制按 1 处理);

4. 传入无效句柄(master_handler 或 slave_handler 为 nil)将返回 nil;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定任务所属的主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
         必须与 slave_handler 属于同一主站上下文;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

slave_handler

参数含义:表示通过 modbus.add_slave 创建的从站句柄,用于指定要查询状态的目标从站;
数据类型:userdata  nil
取值范围:必须是由 modbus.add_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站必须已成功添加到对应主站;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 向 mb_tcp 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
         slave = modbus.add_slave(mb_tcp, 1)
         -- 将创建的 slave 从站句柄传给 slave_handler;
         slave_handler = slave

reg_type

参数含义:表示指定操作的寄存器类型;
数据类型:number
取值范围:modbus.COIL_STATUS(线圈);
         modbus.INPUT_STATUS(离散输入);
         modbus.INPUT_REGISTER(输入寄存器);
         modbus.HOLDING_REGISTER(保持寄存器);
是否必选:必须传入此参数;
注意事项:与 opt_type 组合决定最终 Modbus 功能码(如 READ + HOLDING_REGISTER  功能码 03);
参数示例:-- 指定操作的寄存器类型为 modbus.HOLDING_REGISTER(保持寄存器);
         reg_type = modbus.HOLDING_REGISTER

opt_type

参数含义:表示指定操作类型(读或写);
数据类型:number
取值范围:modbus.READ(读操作);
         modbus.WRITE(写操作);
         modbus.WRITE_SINGLE(写单个寄存器/线圈操作);
是否必选:必须传入此参数;
注意事项:若为 WRITE_SINGLEreg_len 必须为 1(接口会忽略实际传入值,强制按 1 处理);
参数示例:-- 指定某寄存器的操作类型为 modbus.READ(读寄存器);
         opt_type = modbus.READ

reg_addr

参数含义:表示寄存器起始地址(协议地址);
数据类型:number
取值范围:0 ~ 65535
是否必选:必须传入此参数;
注意事项:实际设备地址 = 协议地址 + 偏移(如保持寄存器 40001 对应协议地址 0);
参数示例:-- 指定操作某类型寄存器的起始地址为 0;
         reg_addr = 0

reg_len

参数含义:表示要读取或写入的寄存器数量(线圈/离散输入按“位”计,寄存器按“16位字”计);
数据类型:number
取值范围:1 ~ 120
是否必选:必须传入此参数;
注意事项:WRITE_SINGLE 模式下,此值必须为 1,内部也会强制为 1
         受单次报文长度限制,寄存器数量最多可填写 1 ~ 125 个;
         防止内存越界,底层将寄存器数量限制为 1 ~ 120 个;
参数示例:-- 指定要读取或写入的寄存器数量为 10;
         reg_len = 10

data_addr

参数含义:表示用户数据缓冲区,用于读取或写入操作;
数据类型:userdata(由 zbuff.create 创建);
取值范围:有效 zbuff 缓冲区;
是否必选:必须传入此参数;
注意事项:缓冲区大小必须足够容纳 reg_len 对应的数据量,设置的缓冲区大小不够时,内部也会自动扩容;
参数示例:-- 创建一个 20 字节的数据缓冲区;
         data_addr = zbuff.create(20)

comm_period

参数含义:表示该条消息的通信执行周期(以主站调度周期为单位);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 1);
注意事项:仅对 modbus.LOOP(自动执行模式)模式有效;
         comm_period = 1  = 0 均表示每个调度周期都执行;
         modbus.EXEC(手动执行模式)或 modbus.SINGLE(单次模式,通讯成功即释放句柄)模式下此参数被忽略;
参数示例:-- 设置该条消息的通信执行周期为 1,即每周期都执行;
         comm_period = 1

comm_mode

参数含义:表示该条消息的任务执行模式;
数据类型:number
取值范围:modbus.LOOP(自动执行模式);
         modbus.EXEC(手动执行模式);
         modbus.SINGLE(单次模式,通讯成功即释放句柄);
是否必选:可选传入此参数(默认为 modbus.LOOP);
注意事项:modbus.SINGLE 模式任务执行一次后自动销毁,不可重复使用;
         modbus.EXEC 模式任务不会自动执行,必须手动调用 modbus.exec
参数示例:-- 设置该条消息的任务执行模式为 modbus.LOOP(自动执行模式);
         comm_mode = modbus.LOOP

返回值

local msg = modbus.create_msg(master_handler, slave_handler, reg_type, opt_type, reg_addr, reg_len, data_addr, comm_period,comm_mode)

有一个返回值 msg;

msg

含义说明:表示向指定 Modbus 从站创建并注册一条通信任务(消息)的创建情况;
数据类型:userdata  nil
取值范围:无;
注意事项:无;
返回示例:创建成功时 msg 为一个 userdata 类型的消息对象;
         创建失败时 msg  nil

示例

-- 创建一个 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)

-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus RTU 从站;
slave = modbus.add_slave(mb_rtu, 1)

-- 创建20字节缓冲区;
zbuf = zbuff.create(20)

-- 配置读保持寄存器,自动循环模式;
msg1 = modbus.create_msg(
    mb_rtu,
    slave,
    modbus.HOLDING_REGISTER,  -- 寄存器类型
    modbus.READ,              -- 读操作
    0,                        -- 起始地址
    10,                       -- 读10个寄存器(20字节)
    zbuf,                     -- 数据缓冲区
    1,                        -- 每周期执行
    modbus.LOOP               -- 自动模式
)

4.14 modbus.set_msg_comm_period(msg_handler, comm_period)

功能

表示动态设置指定 Modbus 通信任务(消息)的执行周期;

该周期以主站调度周期为单位,控制任务在自动模式(modbus.LOOP)下的执行频率;

注意事项

1. 仅对 comm_mode = modbus.LOOP 的任务生效;对 modbus.EXEC 或 modbus.SINGLE 模式任务无效(因其不参与周期调度);

2. comm_period = 1 表示每个主站调度周期都执行一次;comm_period = 5 表示每 5 个调度周期执行一次;

3. 主站调度周期由 modbus.set_comm_interval_time 设置(如 100ms),因此实际执行间隔 = comm_period × comm_interval_time;

4. 传入无效 msg_handler 将导致 Lua 运行时错误;

参数

msg_handler

参数含义:表示通过 modbus.create_msg 创建的通信任务句柄,用于指定要调整周期的目标任务;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_msg 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该任务必须属于一个已创建的主站;
         即使主站未启动,也可调用此接口设置周期,启动后生效;
         对非 modbus.LOOP 模式任务设置周期无实际效果;
参数示例:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         msg = modbus.create_msg(mb_tcp, slave, modbus.COIL_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.EXEC)
         -- 将创建的 msg 通信任务句柄传给 msg_handler;
         msg_handler = msg

comm_period

参数含义:表示该条消息的通信执行周期(以主站调度周期为单位);
数据类型:number
取值范围:0 ~ 65535
是否必选:可选传入此参数(默认为 1);
注意事项:实际时间间隔 = comm_period × 主站通讯间隔(comm_interval_time);
参数示例:-- 设置该条消息的通信执行周期为 1,即每周期都执行;
         comm_period = 1

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建一个 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)

-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus RTU 从站;
slave = modbus.add_slave(mb_rtu, 1)

-- 创建20字节缓冲区;
zbuf = zbuff.create(20)

-- 配置读保持寄存器,自动循环模式;
msg = modbus.create_msg(
    mb_rtu,
    slave,
    modbus.HOLDING_REGISTER,  -- 寄存器类型
    modbus.READ,              -- 读操作
    0,                        -- 起始地址
    10,                       -- 读10个寄存器(20字节)
    zbuf,                     -- 数据缓冲区
    1,                        -- 每周期执行
    modbus.LOOP               -- 自动模式
)

-- 启动 mb_rtu 主站;
modbus.master_start(mb_rtu)

-- 动态调整周期:从每周期执行 → 每5个周期执行一次;
-- 若 comm_interval_time = 100ms,则实际间隔等于 500ms;
modbus.set_msg_comm_period(msg, 5)

4.15 modbus.remove_msg(master_handler, slave_handler, msg_handler)

功能

表示从指定 Modbus 从站中删除一条或全部通信任务(消息);

注意事项

1. 删除操作会在当前请求周期结束后生效,被删除的消息将不再参与调度或手动执行;

2. 可在主站运行(master_start 后)或停止状态下调用,均有效;

参数

master_handler

参数含义:表示通过 modbus.create_master 创建的 Modbus 主站句柄,用于指定消息所属的主站;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_master 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该句柄必须已成功创建且未被释放;
         必须与 slave_handler 属于同一主站上下文;
         主站可处于运行或停止状态;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 将创建的 mb_tcp 主站句柄传给 master_handler;
         master_handler = mb_tcp

slave_handler

参数含义:表示通过 modbus.add_slave 创建的从站句柄,用于指定清理消息的目标从站;
数据类型:userdata  nil
取值范围:必须是由 modbus.add_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站必须已成功添加到对应主站;
         即使从站无任何消息,调用也不会报错;
参数示例:-- 创建 Modbus TCP 主站;
         mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
         -- 向 mb_tcp 主站创建并添加一个从站地址为 1 的 Modbus TCP 从站;
         slave = modbus.add_slave(mb_tcp, 1)
         -- 将创建的 slave 从站句柄传给 slave_handler;
         slave_handler = slave

msg_handler

参数含义:表示通过 modbus.create_msg 创建的通信任务句柄,若提供,则仅删除该消息;
         若省略或为 nil,则删除该从站所有消息;
数据类型:userdata  nil
取值范围:有效消息句柄(userdata)时表示删除该消息;
         nil 时表示删除该从站所有消息;
是否必选:可选传入此参数(默认为空);
注意事项:该句柄必须已成功创建且未被释放;
参数示例:-- 向指定从站创建并添加一条 Modbus 通信任务(消息);
         msg = modbus.create_msg(mb_tcp, slave, modbus.COIL_STATUS, modbus.READ, 0, 10, zbuf, 1, modbus.EXEC)
         -- 将创建的 msg 通信任务句柄传给 msg_handler;
         msg_handler = msg

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建一个 Modbus RTU 主站;
mb_rtu = modbus.create_master(modbus.MODBUS_RTU, 1, 9600)

-- 向 mb_rtu 主站创建并添加一个从站地址为 1 的 Modbus RTU 从站;
slave = modbus.add_slave(mb_rtu, 1)

-- 创建20字节缓冲区;
zbuf = zbuff.create(20)

-- 配置读保持寄存器,自动循环模式;
msg = modbus.create_msg(
    mb_rtu,
    slave,
    modbus.HOLDING_REGISTER,  -- 寄存器类型
    modbus.READ,              -- 读操作
    0,                        -- 起始地址
    10,                       -- 读10个寄存器(20字节)
    zbuf,                     -- 数据缓冲区
    1,                        -- 每周期执行
    modbus.LOOP               -- 自动模式
)

-- 示例1:删除 slave 从站的某一条消息
modbus.remove_msg(mb_rtu, slave, msg)

-- 示例2:删除 slave 从站的所有消息(批量清理)
modbus.remove_msg(mb_rtu, slave)

4.16 modbus.create_slave(type, slave_id, uartid_port, adapter_index_baud_rate)

功能

表示创建一个 Modbus 从站(Slave)句柄,用于在设备上实现 Modbus 从站服务(即响应主站请求);

注意事项

1. 该接口用于构建 Modbus 从站服务,不是用于主站通信;

2. 需要提前根据 Modbus RTU/ASCII/TCP 通信模式配置对应的 UART 参数和网络;

3. 创建后,还需调用从站启动接口 modbus.slave_start 才能真正响应请求;

参数

type

参数含义:表示指定 Modbus 主站使用的通信模式类型;
数据类型:number
取值范围:modbus.MODBUS_RTU  modbus.MODBUS_ASCII  modbus.MODBUS_TCP
是否必选:必须传入此参数;
注意事项:若传入无效值,函数将返回 nil
参数示例:-- 指定从站采用 Modbus RTU 通信模式;
         type = modbus.MODBUS_RTU

slave_id

参数含义:表示本设备作为 Modbus 从站的站地址(设备 ID);
数据类型:number
取值范围:1 ~ 247
是否必选:必须传入此参数;
注意事项:主站将使用此地址寻址本设备;
         支持响应广播地址 0
参数示例:-- 设置从站地址为 1;
         slave_id = 1

uartid_port

参数含义:Modbus RTU/ASCII 通信模式时表示本地 UART 设备 ID(如 1);
         Modbus TCP 通信模式时表示本地监听端口号(如 502);
数据类型:number
取值范围:UART ID:平台支持的串口编号,如 0/1/2,具体看硬件;
         端口号:1 ~ 65535
是否必选:必须传入此参数;
注意事项:Modbus RTU/ASCII 通信模式时必须确认该 UART 已通过 uart.setup 初始化;
         Modbus TCP 通信模式时端口号不能被其他服务占用;
参数示例:-- Modbus RTU 或 Modbus ASCII 通信模式时,配置 uartid_port 为串口 1;
         uartid_port = 1
         -- Modbus TCP 通信模式时,配置 uartid_port 为端口号 502;
         uartid_port = 502

adapter_index_baud_rate

参数含义:Modbus RTU/ASCII 通信模式时表示串口波特率(如 9600);
         Modbus TCP 通信模式时表示网卡适配器索引(如 socket.LWIP_ETH);
数据类型:number
取值范围:波特率:1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 等;
         网卡索引:平台定义的适配器编号,可在 socket 核心库 API 文档中查阅;
是否必选:Modbus RTU/ASCII 通信模式时必须传入此参数;
         Modbus TCP 通信模式时可选传入此参数(默认为最后一个注册的网卡适配器序号);
注意事项:Modbus RTU/ASCII 通信模式时串口波特率必须与主站一致,且与 uart.setup 配置匹配;
参数示例:-- Modbus RTU 或 Modbus ASCII 通信模式时,配置 adapter_index_baud_rate 为串口波特率 9600;
         adapter_index_baud_rate = 9600
         -- Modbus TCP 通信模式时,配置 adapter_index_baud_rate 为 LwIP 协议栈的以太网卡;
         adapter_index_baud_rate = socket.LWIP_ETH

返回值

local mb_xxx_s = modbus.create_slave(type, slave_id, uartid_port, adapter_index_baud_rate)

有一个返回值 mb_xxx_s;

mb_xxx_s

含义说明:表示创建 Modbus 从站(Slave)成功时返回其句柄(上下文对象),用于后续操作;
         创建失败时返回 nil
数值类型:userdata  nil
取值范围:无特别限制;
注意事项:必须显式检查返回值是否为 nil,避免对无效句柄进行后续操作,否则将导致 Lua 运行时错误;
返回示例:创建成功时为一个 userdata 类型的对象;
         创建失败时为 nil

示例

-- 示例1:创建 Modbus RTU 从站(本设备作为从站,地址 1,UART 1,9600 bps);
-- 先初始化串口;
uart.setup(1, 9600, 8, 1, uart.NONE, uart.LSB, 1024, 17, 0, 2000)
mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, 1, 1, 9600)

-- 示例2:创建 Modbus TCP 从站(本设备作为从站,地址 1,监听 502 端口,使用有线网卡);
mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502, socket.LWIP_ETH)

4.17 modbus.add_block(slave_handler, reg_type, reg_addr, reg_len, data_addr)

功能

表示为指定的 Modbus 从站(Slave)绑定一块用户数据内存区(寄存器块),用于响应主站的读/写请求;

注意事项

1. 必须在 modbus.create_slave 成功后调用;

2. data_addr 缓冲区大小需要大于所需存储空间,若比所需存储空间小,内部会自动进行扩容;

参数

slave_handler

参数含义:表示通过 modbus.create_slave 创建的从站句柄,用于指定要配置的数据映射目标;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:必须是本设备作为从站的句柄(由 modbus.create_slave 接口成功返回),不是主站侧的从站句柄;
参数示例:-- 创建 Modbus TCP 从站;
         mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502)
         -- 将创建好的 mb_tcp_s 从站句柄传给 slave_handler;
         slave_handler = mb_tcp_s

reg_type

参数含义:表示指定要映射的寄存器类型;
数据类型:number
取值范围:modbus.COIL_STATUS(线圈);
         modbus.INPUT_STATUS(离散输入);
         modbus.INPUT_REGISTER(输入寄存器);
         modbus.HOLDING_REGISTER(保持寄存器);
是否必选:必须传入此参数;
注意事项:从站将根据此类型决定是否允许写入(如 INPUT_REGISTER 只读);
         主站使用对应功能码访问(如读保持寄存器用功能码 03);
参数示例:-- 指定要映射的寄存器类型为 modbus.HOLDING_REGISTER(保持寄存器);
         reg_type = modbus.HOLDING_REGISTER

reg_addr

参数含义:表示寄存器块在 Modbus 地址空间中的起始地址(协议地址,从 0 开始);
数据类型:number
取值范围:0 ~ 65535
是否必选:必须传入此参数;
注意事项:实际设备地址 = 协议地址 + 偏移(如保持寄存器 40001 对应协议地址 0);
参数示例:-- 指定某类型寄存器块在 Modbus 地址空间中的起始地址为 0;
         reg_addr = 0

reg_len

参数含义:表示寄存器块包含的寄存器数量(线圈/离散输入按“位”计,寄存器按“16位字”计);
数据类型:number
取值范围:1 ~ 120
是否必选:必须传入此参数;
注意事项:受单次报文长度限制,寄存器数量最多可填写 1 ~ 125 个;
         防止内存越界,底层将寄存器数量限制为 1 ~ 120 个;
参数示例:-- 指定某个寄存器块包含的寄存器数量为 10;
         reg_len = 10

data_addr

参数含义:表示用户数据缓冲区,用于存储该寄存器块的实际数据;
数据类型:userdata(由 zbuff.create 创建);
取值范围:有效 zbuff 缓冲区;
是否必选:必须传入此参数;
注意事项:缓冲区大小必须足够容纳 reg_len 对应的数据量,设置的缓冲区大小不够时,内部也会自动扩容;
参数示例:-- 创建一个 20 字节的数据缓冲区;
         data_addr = zbuff.create(20)

返回值

local add_block_state = modbus.add_block(slave_handler, reg_type, reg_addr, reg_len, data_addr)

有一个返回值 add_block_state;

add_block_state

含义说明:表示寄存器块添加状态;
数值类型:boolean
取值范围:true  false
注意事项:无;
返回示例:添加成功时 add_block_state  true
         添加失败时 add_block_state  false

示例

-- 创建一个 Modbus RTU 从站;
mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, 1, 1, 9600)

-- 创建数据缓冲区(20字节 = 10个寄存器);
registers = zbuff.create(20)

-- 映射保持寄存器 40001~40010(协议地址 0~9)
local add_block_state = modbus.add_block(mb_rtu_s, modbus.HOLDING_REGISTER, 0, 10, registers)

if not add_block_state then
    log.info("添加寄存器块失败")
    return
end

4.18 modbus.slave_start(slave_handler)

功能

表示启动指定的 Modbus 从站协议栈,使其开始监听并响应主站的 Modbus 请求;

调用后,从站进入工作状态,可根据已通过 modbus.add_block 映射的寄存器块自动处理读/写请求;

注意事项

1. 必须在 modbus.create_slave 成功创建从站后调用;

2. 传入无效 slave_handler 将导致 Lua 运行时错误;

参数

slave_handler

参数含义:表示通过 modbus.create_slave 创建的 Modbus 从站句柄,用于指定要启动的从站实例;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站必须已完成必要配置(如 Modbus RTU 模式需 UART 已初始化,Modbus TCP 模式需网络可用);
         一个设备可同时启动多个从站(如 Modbus RTU + Modbus TCP);
参数示例:-- 创建 Modbus TCP 从站;
         mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502)
         -- 将创建好的 mb_tcp_s 从站句柄传给 slave_handler;
         slave_handler = mb_tcp_s

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 1. 初始化串口 1;
uart.setup(1, 9600, 8, 1, uart.NONE, uart.LSB, 1024, 17, 0, 2000)

-- 2. 创建一个 Modbus RTU 从站;
mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, 1, 1, 9600)
if not mb_rtu_s then
    log.error("从站创建失败")
    return
end

-- 3. 映射寄存器块;
buf = zbuff.create(20)
modbus.add_block(slave, modbus.HOLDING_REGISTER, 0, 10, buf)

-- 4. 启动 mb_rtu_s 从站协议栈;
modbus.slave_start(mb_rtu_s)

log.info("Modbus 从站已启动,等待主站请求")

4.19 modbus.slave_stop(slave_handler)

功能

表示停止指定的 Modbus 从站协议栈运行,使其不再监听或响应任何 Modbus 主站请求;

注意事项

1. 必须在 modbus.slave_start 成功启动后调用才有效;

2. 停止后,从站句柄仍有效,可再次调用 modbus.slave_start 重新启用;

3. 传入无效 slave_handler 将导致 Lua 运行时错误;

参数

slave_handler

参数含义:表示通过 modbus.create_slave 创建的 Modbus 从站句柄,用于指定要停止的从站实例;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:该从站应处于“已启动”状态(即已调用 modbus.slave_start),否则停止操作无实际效果;
         停止后,不应再依赖该从站响应主站请求;
参数示例:-- 创建 Modbus TCP 从站;
         mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502)
         -- 启动 mb_tcp_s 从站协议栈;
         modbus.slave_start(mb_tcp_s)
         -- 将 mb_tcp_s 从站句柄传给 slave_handler 用于停止对应从站协议栈;
         slave_handler = mb_tcp_s

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 创建并启动 mb_rtu_s 从站;
uart.setup(1, 9600, 8, 1, uart.NONE, uart.LSB, 1024, 17, 0, 2000)
mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, 1, 1, 9600)
buf = zbuff.create(20)
modbus.add_block(mb_rtu_s, modbus.HOLDING_REGISTER, 0, 10, buf)
modbus.slave_start(mb_rtu_s)

-- ... 运行一段时间 ...

-- 停止 mb_rtu_s 从站;
modbus.slave_stop(mb_rtu_s)

-- 可选:后续可重新启动
-- modbus.slave_start(mb_rtu_s)

4.20 modbus.slave_on(slave_handler, cb)

功能

表示为指定的 Modbus 从站注册一个事件回调函数,当从站接收到主站的读/写请求时,在处理请求完成后触发该回调;

注意事项

1. 传入无效 slave_handler 将导致 Lua 运行时错误;

参数

slave_handler

参数含义:表示通过 modbus.create_slave 创建的 Modbus 从站句柄,用于指定要注册回调的从站实例;
数据类型:userdata  nil
取值范围:必须是由 modbus.create_slave 接口成功返回的有效句柄;
         传入 nil 或无效值将导致运行时错误;
是否必选:必须传入此参数;
注意事项:必须是本设备作为从站的句柄(由 create_slave 返回);
参数示例:-- 创建 Modbus TCP 从站;
         mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, 1, 502)
         -- 启动 mb_tcp_s 从站协议栈;
         modbus.slave_start(mb_tcp_s)
         -- 将 mb_tcp_s 从站句柄传给 slave_handler 用于指定要注册回调的从站;
         slave_handler = mb_tcp_s

cb

参数含义:Modbus 从站事件回调函数;回调函数的格式为:
         function callback(slave_handler, reg_type, opt_type, reg_addr, reg_len)
            log.info("callback", slave_handler, reg_type, opt_type, reg_addr, reg_len)
         end
         该回调函数接收 slave_handlerreg_typeopt_typereg_addrreg_len 五个参数,在不同事件类型下参数含义有所不同:

         -- 参数含义:表示 Modbus 从站对象本身;
         -- 数据类型:userdata;
         -- 取值范围:无限制;
         -- 是否必选:必须传入此参数;
         -- 注意事项:所有回调事件都会传递此参数,可用于在回调中识别具体从站实例(当存在多个从站时);
         slave_handler

         -- 参数含义:表示请求操作的寄存器类型标识符;
         -- 数据类型:number;
         -- 取值范围:-- 0,对应常量 modbus.COIL_STATUS(线圈);
                     -- 1,对应常量 modbus.INPUT_STATUS(离散输入);
                     -- 3,对应常量 modbus.INPUT_REGISTER(输入寄存器);
                     -- 4,对应常量 modbus.HOLDING_REGISTER(保持寄存器);
         -- 是否必选:必须传入此参数;
         -- 注意事项:用于标识当前请求针对的 Modbus 数据区类型,是判断操作对象的核心依据;
         reg_type

         -- 参数含义:表示请求的操作类型;
         -- 数据类型:number;
         -- 取值范围:-- 0,对应常量 modbus.READ(读操作);
                     -- 1,对应常量 modbus.WRITE(写操作);
                     -- 2,对应常量 modbus.WRITE_SINGLE(写单个寄存器操作);
         -- 是否必选:必须传入此参数;
         -- 注意事项:结合 reg_type 可确定具体 Modbus 功能码(如 READ + HOLDING_REGISTER → 功能码 03)
         opt_type

         -- 参数含义:表示请求的寄存器起始地址(协议地址,从 0 开始);
         -- 数据类型:number;
         -- 取值范围:0 ~ 65535;
         -- 是否必选:必须传入此参数;
         -- 注意事项:-- 此为 Modbus 协议地址,不是设备文档中的 40001 等偏移地址;
                     -- 实际设备地址 = 协议地址 + 偏移(如保持寄存器 40001 对应 reg_addr = 0)
         reg_addr

         -- 参数含义:表示请求操作的寄存器数量(线圈/离散输入按“位”计,寄存器按“16位字”计);
         -- 数据类型:number;
         -- 取值范围:1 ~ 120;
         -- 是否必选:必须传入此参数;
         -- 注意事项:对于 WRITE_SINGLE,此值恒为 1;
         --          受单次报文长度限制,寄存器数量最多 1 ~ 125 个;
         --          防止内存越界,底层将寄存器数量限制为 1 ~ 120 个;
         reg_len
数据类型:function
取值范围:回调函数本身无取值范围这一说法;
是否必选:必须传入此参数;
注意事项:每次调试会覆盖之前注册的回调函数;
参数示例:-- 注册 Modbus 从站事件回调函数;
         function modbus_slave_cb(slave_handler, reg_type, opt_type, reg_addr, reg_len)
             -- 用户自定义代码;
             -- 打印所有回调参数;
             log.info("Modbus 回调:", 
                 "从站 = ", slave_handler,
                 "寄存器类型 = ", reg_type,
                 "操作类型 = ", opt_type,
                 "起始地址 = ", reg_addr,
                 "数量 = ", reg_len
             )
         end

         -- Modbus 从站注册回调事件接口,modbus_slave_cb 为回调函数;
         modbus.slave_on(slave, modbus_slave_cb)

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 注册 Modbus 从站事件回调函数;
function modbus_slave_cb(slave_handler, reg_type, opt_type, reg_addr, reg_len)
    -- 用户自定义代码;
    -- 打印所有回调参数;
    log.info("Modbus 回调:",
        "从站 = ", slave_handler,
        "寄存器类型 = ", reg_type,
        "操作类型 = ", opt_type,
        "起始地址 = ", reg_addr,
        "数量 = ", reg_len
    )
end

-- Modbus 从站注册回调事件接口,modbus_slave_cb 为回调函数;
modbus.slave_on(slave, modbus_slave_cb)

4.21 modbus.debug(en)

功能

表示开启或关闭 Modbus 协议栈的调试模式;

启用后,底层 Modbus 引擎将通过系统日志(如 log 或串口)输出详细的通信过程信息,用于开发调试、故障排查和协议交互分析;

注意事项

无;

参数

en

参数含义:表示调试模式开关标志;
数据类型:number
取值范围:1 表示开启;
         0 表示关闭;
是否必选:必须传入此参数;
注意事项:无;
参数示例:-- 开启 Modbus 调试模式;
         modbus.debug(1)
         -- 关闭 Modbus 调试模式;
         modbus.debug(0)

返回值

该接口无返回值,只需调用该接口执行相关操作,无需处理返回结果;

如果一定要把接口调用的结果赋值给一个变量,则这个变量就是一个 nil 值;

示例

-- 开启调试模式;
modbus.debug(1)

-- 创建主站、从站、执行通信...
-- 此时将看到详细的 Modbus 报文日志

-- 调试完成后关闭;
modbus.debug(0)

五、产品支持说明

支持 LuatOS 开发的所有产品都支持 modbus 核心库。