工业物联网场景中,Modbus 因易用、通用的特性,成为各类工业设备的通信首选。原生开发模式下,各类底层通信逻辑需开发者自行实现,开发效率低、隐患多。
LuatOS 自研的exmodbus库,把 Modbus 底层细节全部封装完毕,适配全系列硬件,搭配开发板与示例代码即可快速上手,极大降低了 Modbus 应用开发门槛。本文将结合理论、API 与实战,全面讲解该库的使用。
一、必看的Modbus基础知识
Modbus最初由Modicon(现为施耐德电气旗下品牌)于1979年开发,是一种用于可编程逻辑控制器(PLC)之间通信的工业通信协议。
由于其简单、开放、免费且易于实现,Modbus已成为工业自动化领域最广泛使用的通信协议之一,被广泛应用于工业控制、楼宇自动化、能源管理、智能仪表等场景。
1.1 Modbus三种常见通信模式
Modbus协议采用主从架构,最初基于串行通信(Modbus RTU和Modbus ASCII),后扩展支持以太网传输(Modbus TCP)。
1)Modbus RTU
-
传输介质:RS485/RS232串行通信;
-
编码格式:二进制,报文紧凑;
-
校验方式:CRC16;
-
报文结构:[从站地址][功能码][数据][CRC16校验]
-
典型示例:
请求:01 03 00 00 00 02C40B
响应:01 03 04 00 01 00 05 6B F0
2)Modbus ASCII
-
传输介质:RS485/RS232串行通信;
-
编码格式:ASCII字符,报文可读性强;
-
校验方式:LRC;
-
报文结构:[:][从站地址][功能码][数据][LRC校验][\r\n]
-
典型示例:
:010300000002BA\r\n
3)Modbus TCP
-
传输介质:以太网等;
-
编码格式:二进制,在RTU基础上增加了MBAP头;
-
校验方式:无需校验位,直接通过TCP/IP传输;
-
报文结构:[MBAP头][功能码][数据]
-
典型示例:
请求:00 01 00 00 00 06 01 03 00 00 00 02
p应:00 01 00 00 00 07 01 03 04 00 01 00 05
1.2 Modbus四种基本数据类型
Modbus定义了四种数据对象:线圈、离散输入、输入寄存器和保持寄存器,并通过功能码实现设备间的数据交换。

二 exmodbus库核心API函数速览
LuatOS的exmodbus库从工业应用实际需求出发,将复杂的协议细节封装为简洁易用的API,使开发者能够专注于业务逻辑,从而有效缩短项目周期、降低维护成本。
最新API文档详见: https://docs.openluat.com/osapi/ext/exmodbus/
2.1 exmodbus.create(config)
函数功能: 创建并返回一个新的modbus主/从站实例。
简要示例:
含义说明:modbus 实例对象;
数据类型:table;
取值范围:暂无;
返回值示例:-- 创建 modbus RTU 主站local create_config = {-- 串口配置参数;mode = exmodbus.RTU_MASTER, -- 通信模式:RTU 主站uart_id = 1, -- 串口 ID:uart1baud_rate = 115200, -- 波特率:115200data_bits = 8, -- 数据位:8stop_bits = 1, -- 停止位:1parity_bits = uart.None, -- 校验位:无校验byte_order = uart.LSB, -- 字节顺序:小端序rs485_dir_gpio = 23, -- RS485 方向转换 GPIO 引脚rs485_dir_rx_level = 0 -- RS485 接收方向电平:0 为低电平,1 为高电平}local rtu_master = exmodbus.create(config)
2.2 modbus:read(config)
函数功能: 主站向从站发送读取操作请求(阻塞接口);支持通过“字段参数方式”或“原始帧方式”传入config配置参数。
简要示例:
-- 1. 创建主站-- 2. 执行读取请求local result = modbus:read(read_config)-- 3. 判断从站响应状态if result.status == exmodbus.STATUS_SUCCESS thenlog.info("收到响应数据且数据有效")elseif result.status == exmodbus.STATUS_DATA_INVALID thenlog.info("收到响应数据但数据损坏/校验失败")elseif result.status == exmodbus.STATUS_EXCEPTION thenlog.info("收到 modbus 标准异常响应")elseif result.status == exmodbus.STATUS_TIMEROUT thenlog.info("无任何响应(超时)")end
2.3 modbus:write(config)
函数功能: 主站向从站发送写入操作请求(阻塞接口);支持通过“字段参数方式”或“原始帧方式”传入config配置参数。
简要示例:
-- 1. 创建主站-- 2. 执行写入请求local result = modbus:write(write_config)-- 3. 判断从站响应状态if result.status == exmodbus.STATUS_SUCCESS thenlog.info("收到响应数据且数据有效")elseif result.status == exmodbus.STATUS_DATA_INVALID thenlog.info("收到响应数据但数据损坏/校验失败")elseif result.status == exmodbus.STATUS_EXCEPTION thenlog.info("收到 modbus 标准异常响应")elseif result.status == exmodbus.STATUS_TIMEROUT thenlog.info("无任何响应(超时)")end
2.4 modbus:destroy()
函数功能: 销毁已创建的主/从站示例对象。
简要示例:
1 modbus:destroy()
2.5 modbus:on(callback)
函数功能: 此接口仅限设备做从站时使用;当收到主站请求数据时,通过callback通知应用脚本处理;应用脚本处理完之后,在callback中通知返回值,告知exmodbus扩展库返回给主站。
简要示例:
-- 0. 初始化一些参数
-- 当前从站地址(ID 号)
local SLAVE_ID = 1-- 寄存器映射表(按类型组织)
local modbus_data = {coils = {}, -- 线圈,可读可写,布尔值 (0/1)inputs = {}, -- 输入状态,只读,布尔值 (0/1)input_registers = {}, -- 输入寄存器,只读,16 位无符号整数holding_registers = {} -- 保持寄存器,可读可写,16 位无符号整数
}-- 初始化一些默认值,便于测试
for i = 0, 3 domodbus_data.coils[i] = 0modbus_data.inputs[i] = 1modbus_data.input_registers[i] = 100 + imodbus_data.holding_registers[i] = 200 + i
end-- 1. 创建从站
-- 2. 注册主站请求处理回调函数
local function callback(request)log.info("exmodbus_test", "收到主站请求")-- 检查从站 ID 是否匹配if request.slave_id ~= SLAVE_ID thenlog.info("exmodbus_test", "从站 ID 不匹配,请求从站 ID 为", request.slave_id, ",当前从站 ID 为", SLAVE_ID)return nilend-- 根据功能码和寄存器类型,匹配对应的数据表local data_table = nillocal is_write = false -- 标记是否为写操作-- 检查请求的功能码是否支持if request.func_code == exmodbus.READ_COILS then -- 读线圈data_table = modbus_data.coilselseif request.func_code == exmodbus.READ_DISCRETE_INPUTS then -- 读离散输入data_table = modbus_data.inputselseif request.func_code == exmodbus.READ_HOLDING_REGISTERS then -- 读保持寄存器data_table = modbus_data.holding_registerselseif request.func_code == exmodbus.READ_INPUT_REGISTERS then -- 读输入寄存器data_table = modbus_data.input_registerselseif request.func_code == exmodbus.WRITE_SINGLE_COIL or request.func_code == exmodbus.WRITE_MULTIPLE_COILS then -- 写单个/多个线圈is_write = truedata_table = modbus_data.coilselseif request.func_code == exmodbus.WRITE_SINGLE_HOLDING_REGISTER or request.func_code == exmodbus.WRITE_MULTIPLE_HOLDING_REGISTERS then -- 写单个/多个保持寄存器is_write = truedata_table = modbus_data.holding_registerselse-- 不支持的功能码log.info("exmodbus_test", "不支持的功能码: ", request.func_code)return exmodbus.ILLEGAL_FUNCTIONend-- 检查数据地址是否有效local end_addr = request.start_addr + request.reg_count - 1-- 假设每种寄存器的最大地址是 3 (即 0 - 3)if request.start_addr < 0 or end_addr > 3 thenlog.info("exmodbus_test", "数据地址超出范围,起始地址为", request.start_addr, "结束地址为", end_addr)return exmodbus.ILLEGAL_DATA_ADDRESSend-- 处理读取操作if not is_write then-- 构造响应数据表local response = {}for i = 0, request.reg_count - 1 dolocal addr = request.start_addr + iresponse[addr] = data_table[addr]endlog.info("exmodbus_test", "读取成功,返回数据: ", table.concat(response, ", "))return responseend-- 处理写入操作if is_write then-- 执行写入操作for i = 0, request.reg_count - 1 dolocal addr = request.start_addr + idata_table[addr] = request.data[addr]log.info("exmodbus_test", "写入成功,写入地址: ", addr, "写入数据: ", request.data[addr])endreturn {} -- 返回空表表示成功end
end-- 3. 注册主站请求处理回调函数
modbus:on(callback)
2.6 exmodbus.debug(enable)
函数功能: 设置debug开关,开启后会打印接收和发送的原始数据。
简要示例:
1-- 开启 debug模式;
2 exmodbus.debug(true)
3
4-- 关闭debug模式;
5 exmodbus.debug(false)
6
三 开源示例快速上手
为帮助行业客户快速评估和落地,合宙在LuatOS官方仓库的demo/modbus目录下提供了完整的开源示例代码,涵盖TCP主站/从站应用、RTU主站/从站应用、485温湿度传感器读取等功能模块。
以Air8000系列开发板为例:
-
Modbus最新示例源码:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/modbus
-
Modbus实操教程详见:https://docs.openluat.com/air8000/luatos/app/modbus/
LuatOS提供一站式物联网高效解决方案,集成高速通信、外设驱动、UI交互及视觉处理,助力行业客户快速落地产品,同时实现极致低功耗——相较于传统的“串口屏+DTU方案”或安卓方案,功耗减半,成本不足三分之一。
