工业 Modbus 通信开发利器 exmodbus
作为工业通信的标准协议,Modbus 大量应用于温湿度传感器、PLC、变频器等设备。传统开发方式要处理大量底层通信细节,增加了开发工作量与出错概率。
针对这一行业痛点,LuatOS 上线exmodbus扩展库,一站式封装协议底层能力,完美兼容合宙 LuatOS 模组及工业引擎产品,依托配套硬件与开源案例,助力开发者快速完成 Modbus 通信功能开发。
一、必看的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:uart1
baud_rate = 115200, -- 波特率:115200
data_bits = 8, -- 数据位:8
stop_bits = 1, -- 停止位:1
parity_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 then
log.info("收到响应数据且数据有效")
elseif result.status == exmodbus.STATUS_DATA_INVALID then
log.info("收到响应数据但数据损坏/校验失败")
elseif result.status == exmodbus.STATUS_EXCEPTION then
log.info("收到 modbus 标准异常响应")
elseif result.status == exmodbus.STATUS_TIMEROUT then
log.info("无任何响应(超时)")
end
2.3 modbus:write(config)
函数功能: 主站向从站发送写入操作请求(阻塞接口);支持通过“字段参数方式”或“原始帧方式”传入config配置参数。
简要示例:
-- 1. 创建主站
-- 2. 执行写入请求
local result = modbus:write(write_config)
-- 3. 判断从站响应状态
if result.status == exmodbus.STATUS_SUCCESS then
log.info("收到响应数据且数据有效")
elseif result.status == exmodbus.STATUS_DATA_INVALID then
log.info("收到响应数据但数据损坏/校验失败")
elseif result.status == exmodbus.STATUS_EXCEPTION then
log.info("收到 modbus 标准异常响应")
elseif result.status == exmodbus.STATUS_TIMEROUT then
log.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 do
modbus_data.coils[i] = 0
modbus_data.inputs[i] = 1
modbus_data.input_registers[i] = 100 + i
modbus_data.holding_registers[i] = 200 + i
end
-- 1. 创建从站
-- 2. 注册主站请求处理回调函数
local function callback(request)
log.info("exmodbus_test", "收到主站请求")
-- 检查从站 ID 是否匹配
if request.slave_id ~= SLAVE_ID then
log.info("exmodbus_test", "从站 ID 不匹配,请求从站 ID 为", request.slave_id, ",当前从站 ID 为", SLAVE_ID)
return nil
end
-- 根据功能码和寄存器类型,匹配对应的数据表
local data_table = nil
local is_write = false -- 标记是否为写操作
-- 检查请求的功能码是否支持
if request.func_code == exmodbus.READ_COILS then -- 读线圈
data_table = modbus_data.coils
elseif request.func_code == exmodbus.READ_DISCRETE_INPUTS then -- 读离散输入
data_table = modbus_data.inputs
elseif request.func_code == exmodbus.READ_HOLDING_REGISTERS then -- 读保持寄存器
data_table = modbus_data.holding_registers
elseif request.func_code == exmodbus.READ_INPUT_REGISTERS then -- 读输入寄存器
data_table = modbus_data.input_registers
elseif request.func_code == exmodbus.WRITE_SINGLE_COIL or request.func_code == exmodbus.WRITE_MULTIPLE_COILS then -- 写单个/多个线圈
is_write = true
data_table = modbus_data.coils
elseif request.func_code == exmodbus.WRITE_SINGLE_HOLDING_REGISTER or request.func_code == exmodbus.WRITE_MULTIPLE_HOLDING_REGISTERS then -- 写单个/多个保持寄存器
is_write = true
data_table = modbus_data.holding_registers
else
-- 不支持的功能码
log.info("exmodbus_test", "不支持的功能码: ", request.func_code)
return exmodbus.ILLEGAL_FUNCTION
end
-- 检查数据地址是否有效
local end_addr = request.start_addr + request.reg_count - 1
-- 假设每种寄存器的最大地址是 3 (即 0 - 3)
if request.start_addr < 0 or end_addr > 3 then
log.info("exmodbus_test", "数据地址超出范围,起始地址为", request.start_addr, "结束地址为", end_addr)
return exmodbus.ILLEGAL_DATA_ADDRESS
end
-- 处理读取操作
if not is_write then
-- 构造响应数据表
local response = {}
for i = 0, request.reg_count - 1 do
local addr = request.start_addr + i
response[addr] = data_table[addr]
end
log.info("exmodbus_test", "读取成功,返回数据: ", table.concat(response, ", "))
return response
end
-- 处理写入操作
if is_write then
-- 执行写入操作
for i = 0, request.reg_count - 1 do
local addr = request.start_addr + i
data_table[addr] = request.data[addr]
log.info("exmodbus_test", "写入成功,写入地址: ", addr, "写入数据: ", request.data[addr])
end
return {} -- 返回空表表示成功
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/
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)