想象一下,你要和一台工业设备"对话",比如询问温度传感器"现在多少度?“或者告诉电机"转快一点”。
Modbus RTU就是这种"对话"的标准语言,就像人与人之间说普通话一样。

它采用主从结构,就像老师和学生的关系:

  • 主站(老师):你的Java程序,负责提问和下达指令
  • 从站(学生):各种工业设备,只能回答问题和执行指令

本文将通过XYIoT项目的实际代码,手把手教你如何用Java实现这种"对话"。

1. 核心依赖与配置

1.1 Maven依赖配置

就像做菜需要准备食材一样,我们先准备好必要的依赖包:

<!-- Modbus串口通信相关依赖 -->
<dependency><groupId>com.intelligt.modbus</groupId><artifactId>jlibmodbus</artifactId><version>1.2.9.7</version>
</dependency><!-- 串口通信依赖 -->
<dependency><groupId>org.scream3r</groupId><artifactId>jssc</artifactId><version>2.8.0</version>
</dependency>

1.2 串口参数配置

串口通信就像打电话,需要设置正确的"电话号码"和"通话规则":

modbus:serial:# 串口名称port-name: COM1# 波特率baud-rate: 9600# 数据位(8位)data-bits: 8# 停止位(1位)stop-bits: 1# 校验位(0-NONE, 1-ODD, 2-EVEN)parity: 0# 超时时间(毫秒)timeout: 2000# 设备地址device-address: 1

2. 核心工具类实现

2.1 配置类

这个配置类就像是"通讯录",记录了如何连接设备:

@Data
@Configuration
@ConfigurationProperties(prefix = "modbus.serial")
public class ModbusSerialConfig {private String portName = "COM3";private int baudRate = 9600;private int dataBits = 8;private int stopBits = 1;private int parity = 0;private int timeout = 1000;private int deviceAddress = 1;
}

2.2 串口工具类

这个工具类就像是"翻译官",负责把你的Java指令翻译成设备能懂的Modbus语言:

@Slf4j
@Component
public class ModbusSerialUtil {private static final Map<String, ModbusMaster> CONNECTION_CACHE = new HashMap<>();private static ModbusSerialConfig getConfig() {return SpringUtils.getBean(ModbusSerialConfig.class);}/*** 获取ModbusMaster实例 - 就像获取一个专门的"对讲机"* 支持连接缓存(避免重复拨号)和自动重连(断线自动重拨)*/public static ModbusMaster getMaster(String portName) {ModbusSerialConfig config = getConfig();String port = StringUtils.isEmpty(portName) ? config.getPortName() : portName;log.info("正在连接Modbus串口: {}", port);// 优先使用缓存连接 - 就像手机的通话记录,直接重拨上次的号码if (CONNECTION_CACHE.containsKey(port) && CONNECTION_CACHE.get(port) != null) {ModbusMaster cachedMaster = CONNECTION_CACHE.get(port);try {if (!cachedMaster.isConnected()) {cachedMaster.connect();}return cachedMaster;} catch (Exception e) {log.warn("缓存连接失效: {},重新建立连接", e.getMessage());}}// 建立新的串口连接 - 就像第一次拨打一个新号码try {// 初始化配置SerialParameters serialParameters = new SerialParameters();serialParameters.setDevice(port);serialParameters.setBaudRate(BaudRate.getBaudRate(config.getBaudRate()));serialParameters.setDataBits(config.getDataBits());serialParameters.setStopBits(config.getStopBits());// 设置校验位switch (config.getParity()) {case 1: serialParameters.setParity(Parity.ODD); break;case 2: serialParameters.setParity(Parity.EVEN); break;default: serialParameters.setParity(Parity.NONE); break;}// 设置串口工厂(关键步骤)- 就像选择用哪家电信运营商打电话SerialUtils.setSerialPortFactory(new SerialPortFactoryJSSC());// 创建ModbusMaster实例ModbusMaster master = ModbusMasterFactory.createModbusMasterRTU(serialParameters);master.setResponseTimeout(config.getTimeout());// 连接串口master.connect();// 缓存连接实例 - 把这个"电话号码"存到通讯录里CONNECTION_CACHE.put(port, master);return master;} catch (Exception e) {log.error("创建Modbus串口连接失败", e);throw new ServiceException("串口连接建立失败: " + e.getMessage());}}
}

3. 读写操作实现

3.1 读取保持寄存器

读取寄存器就像问设备"你现在的状态是什么?“比如问温度传感器"现在多少度?”:

public static int[] readHoldingRegisters(int slaveId, int offset, int quantity) {ModbusMaster master = getMaster();try {if (!master.isConnected()) {log.info("检测到连接断开,正在重新连接...");master.connect();// 连接建立后等待设备就绪Thread.sleep(500);}// 实现重试机制 - 就像打电话没接通时会自动重拨几次int maxRetries = 3;for (int retry = 0; retry < maxRetries; retry++) {try {log.info("读取保持寄存器 (第{}/{}次尝试)", retry + 1, maxRetries);int[] result = master.readHoldingRegisters(slaveId, offset, quantity);log.info("数据读取成功: {}", Arrays.toString(result));return result;} catch (ModbusIOException e) {log.warn("IO异常,准备重试: {}", e.getMessage());Thread.sleep(1000);} catch (ModbusProtocolException e) {log.warn("协议异常,准备重试: {}", e.getMessage());Thread.sleep(1000);}}throw new ModbusIOException("多次重试后读取仍然失败");} catch (Exception e) {log.error("保持寄存器读取失败: {}", e.getMessage());throw new ServiceException("保持寄存器读取失败: " + e.getMessage());}
}

3.2 写入单个寄存器

写入寄存器就像给设备下指令,比如告诉空调"设定温度26度":

public static void writeSingleRegister(int slaveId, int offset, int value) {ModbusMaster master = getMaster();try {if (!master.isConnected()) {master.connect();}master.writeSingleRegister(slaveId, offset, value);// 写入后等待设备处理 - 就像发短信后等对方回复"收到"Thread.sleep(500);} catch (Exception e) {log.error("单个寄存器写入失败: {}", e.getMessage());throw new ServiceException("单个寄存器写入失败: " + e.getMessage());}
}

4. 检测可用串口

4.1 系统串口检测

就像查看电脑上有哪些USB接口可以用一样,我们需要找到可用的串口:

public static List<String> getSystemPortNames() {List<String> portList = new ArrayList<>();String osName = System.getProperty("os.name").toLowerCase();// Windows系统串口检测 - 就像在Windows设备管理器里查看COM口if (osName.contains("win")) {try {Process process = Runtime.getRuntime().exec(new String[] {"powershell.exe", "-Command", "[System.IO.Ports.SerialPort]::getportnames()"});try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {line = line.trim();if (!line.isEmpty() && !portList.contains(line)) {portList.add(line);}}}} catch (Exception e) {log.warn("Windows串口检测失败: {}", e.getMessage());}} // Linux系统串口检测 - 就像用ls命令查看/dev目录下的设备文件else if (osName.contains("nix") || osName.contains("nux")) {try {Process process = Runtime.getRuntime().exec("ls -la /dev/tty*");try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {if (line.contains("ttyS") || line.contains("ttyUSB")) {String[] parts = line.split("\\s+");String portName = "/dev/" + parts[parts.length - 1];portList.add(portName);}}}} catch (Exception e) {log.warn("Linux串口检测失败: {}", e.getMessage());}}return portList;
}

5. 测试连接

5.1 连接测试方法

就像打电话前先测试信号是否正常一样,我们需要测试串口连接:

public static boolean testConnection(String portName) {ModbusMaster master = null;try {log.info("正在测试串口连接: {}", portName);ModbusSerialConfig config = getConfig();SerialParameters serialParameters = new SerialParameters();serialParameters.setDevice(portName);serialParameters.setBaudRate(BaudRate.getBaudRate(config.getBaudRate()));serialParameters.setDataBits(config.getDataBits());serialParameters.setStopBits(config.getStopBits());// 设置校验位switch (config.getParity()) {case 1: serialParameters.setParity(Parity.ODD); break;case 2: serialParameters.setParity(Parity.EVEN); break;default: serialParameters.setParity(Parity.NONE); break;}// 设置串口工厂(必需步骤)SerialUtils.setSerialPortFactory(new SerialPortFactoryJSSC());master = ModbusMasterFactory.createModbusMasterRTU(serialParameters);master.setResponseTimeout(config.getTimeout());master.connect();boolean connected = master.isConnected();return connected;} catch (Exception e) {log.error("串口连接测试失败: {}", e.getMessage());return false;} finally {if (master != null) {try {master.disconnect();} catch (Exception e) {log.error("测试连接关闭失败: {}", e.getMessage());}}}
}

6. 实战经验与注意事项

6.1 关键配置要点

这些是实际项目中踩过的坑,就像老司机的驾驶经验:

设置SerialPortFactory

这就像选择正确的"电话线路",不设置就像拿着手机但没插SIM卡:

SerialUtils.setSerialPortFactory(new SerialPortFactoryJSSC());

添加操作间延迟

设备需要"思考时间",就像你问朋友问题后要等他回答,不能连珠炮似的问个不停:

// 写入操作后延迟
Thread.sleep(2000);

增加超时设置

就像打电话设置等待时间,超过这个时间没人接就自动挂断:

master.setResponseTimeout(2000); // 设置2秒超时

实现重试机制

工业现场就像信号不好的地方,有时需要多打几次电话才能接通:

// 最多重试3次
for (int retry = 0; retry < 3; retry++) {try {return master.readHoldingRegisters(slaveId, offset, quantity);} catch (Exception e) {if (retry == 2) throw e;Thread.sleep(1000);}
}

使用连接缓存

就像手机的通话记录,避免每次都重新拨号:

private static final Map<String, ModbusMaster> CONNECTION_CACHE = new HashMap<>();

7. 帧数据解析示例

7.1 通信日志分析

就像查看手机的通话记录一样,我们可以看到每次"对话"的详细内容。

比如你在控制台会看到这样的日志:

以下面Modbus RTU通信日志为例:

Frame sent: 0106000000648821
Frame recv: 0106000000648821

7.2 数据帧结构解析

这串看似乱码的数字,其实就像一封标准格式的信件:

  • 01: 从站地址(设备地址),这里是1
  • 06: 功能码,表示写入单个寄存器
  • 0000: 寄存器地址,这里是0
  • 0064: 写入的值,十六进制0x64即十进制100
  • 8821: CRC校验和

想象Modbus通信就像邮递员送信:

  • 主站:就像邮局,负责发送各种信件(指令)
  • 从站:就像收信人,收到信后按要求办事并回信
  • 地址:就像门牌号,每个设备都有唯一编号
  • 功能码:就像信件类型,告诉收信人要做什么事

7.3 数据帧详细分析

让我们把这个"信件"拆开来看:

数据帧0106000000648821就像一个包裹的标签:

01      - 从站地址(设备编号,这里是1号设备)
06      - 功能码(06表示"写单个寄存器")
0000    - 寄存器地址(这里写入第0号寄存器)
0064    - 要写入的数据(0x64十六进制 = 100十进制)
8821    - CRC校验码(确保数据完整性,类似防伪标记)

7.4 常见功能码说明

功能码就像不同类型的业务单,每种单子办不同的事:

  • 01 - 读取线圈状态(开关量输出)
  • 02 - 读取输入状态(开关量输入)
  • 03 - 读取保持寄存器(可读写的数据)
  • 04 - 读取输入寄存器(只读数据)
  • 05 - 写单个线圈
  • 06 - 写单个寄存器
  • 15 - 写多个线圈
  • 16 - 写多个寄存器

7.5 通信实例分析

让我们看几个实际的"对话"例子:

写入命令示例(就像发指令)

0106000000648821  (写入请求)
0106000000648821  (设备回复确认)

这就像:

  • 你对1号设备说:“把你的第0个参数设置为100”
  • 设备回复:“好的,已经设置为100了”(重复一遍确认收到)

读取命令示例(就像询问状态)

010300000001840A  (读取请求)
0103020064XX      (设备回复,XX是校验码)

这就像:

  • 你问1号设备:“告诉我你第0个参数的值是多少?”
  • 设备回答:“我是1号设备,你问的参数值是100”

7.6 常见通信问题分析

有时候"电话"会出现问题,比如:

Frame sent: 010300000001840A
Frame recv: 0103000000          (应该还有后续数据)

就像打电话时信号不好,可能的原因:

  1. 对方正忙,需要时间处理(设备处理指令需要时间)
  2. 信号干扰,话说了一半就断了(数据传输被干扰)
  3. 对方有特殊的接电话习惯(设备有特定时序要求)

所以我们要像打电话一样,说完一句话等对方回应,不要急着说下一句。

7.7 数据类型说明

Modbus就像一个只会说数字的"外国人",它能表达的内容有限:

  • 线圈/离散输入:只会说"是"或"不是"(开关状态)
  • 寄存器:会说0到65535的整数(像计数器)
  • 连续寄存器:把两个数字拼起来说更大的数或小数

8. 总结

通过本文的学习,你已经掌握了用Java与工业设备"对话"的技能:

就像学会打电话

  • 知道怎么拨号(配置串口参数)
  • 会存通讯录(连接缓存)
  • 信号不好时会重拨(重试机制)
  • 知道什么时候该等对方说话(延迟控制)
  • 会测试电话是否通畅(连接测试)

实际应用场景

  • 工厂自动化:让你的程序控制生产线设备
  • 环境监测:实时读取温湿度、压力等传感器数据
  • 能源管理:监控和控制电力设备的运行状态=

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96053.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96053.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/96053.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS安装或升级protoc

卸载旧版本 sudo yum remove protobuf protobuf-c protobuf-compiler -y sudo rm -f /usr/bin/protoc sudo rm -rf /usr/include/google/protobuf 下载 wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.0/protoc-3.15.0-linux-x86_64.zip unz…

人工智能在医学图像中的应用:从机器学习到深度学习

目的&#xff1a;人工智能&#xff08;AI&#xff09;模型在生物医学研究和医疗服务中扮演着越来越重要的角色。本综述聚焦于在现实世界背景下&#xff0c;开发AI应用作为临床决策支持系统时需要澄清的挑战性问题。方法&#xff1a;进行了一项叙述性综述&#xff0c;包含对1989…

基于Echarts+HTML5可视化数据大屏展示-智慧小区大数据分析

效果展示&#xff1a;代码结构&#xff1a;主要代码实现 index.html布局 <!doctype html> <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title>智慧农业大数据展示</title><link rel"s…

【LeetCode热题100道笔记】验证二叉搜索树

题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 严格小于 当前节点的数。 节点的右子树只包含 严格大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。 示例 1&…

Apache Tomcat 教程:从入门到精通(含目录结构与版本详解)

​​​​​​1. 背景​​ Apache Tomcat 是一个开源的 ​​Java Servlet 容器​​&#xff0c;由 ​​Apache 软件基金会&#xff08;ASF&#xff09;​​ 开发和维护&#xff0c;最初由 ​​Sun Microsystems​​ 的软件架构师 ​​James Duncan Davidson​​ 设计&#xff0…

设计模式从入门到精通之(六)策略模式

策略模式&#xff1a;让算法灵活切换的秘密武器在日常开发中&#xff0c;算法的选择常常是程序设计的核心&#xff0c;比如支付方式的选择、排序逻辑的切换、促销活动的动态调整等。当需求变化时&#xff0c;我们需要在多个算法之间切换&#xff0c;但又不希望修改已有代码。如…

安装MATLAB205软件记录

安装MATLAB2025 一台电脑可以安装多个版本的MATLAB; 下载资源 微信公众平台-MATLAB R2025a v25.1下载及安装教程 安装步骤 解压, 压缩文件大小为13.8GB 装载 选中setup.exe右键单击以管理员身份运行 我有文件安装密钥 接受许可条款 复制粘贴密钥 63733-59078-50866-02827-…

MySQL 基础架构(一):SQL语句的执行之旅

MySQL系列文章 MySQL 基础架构&#xff08;一&#xff09;&#xff1a;SQL语句的执行之旅 你是否好奇过&#xff0c;一条看似简单的SQL查询语句&#xff0c;在MySQL内部究竟经历了怎样的"奇幻之旅"&#xff1f;从连接建立到结果返回&#xff0c;MySQL是如何层层处理、…

Spring Boot 使用 Druid 连接池极致优化

在 Spring Boot 中使用 Druid 连接池进行极致优化&#xff0c;需要从核心参数调优、监控体系搭建、安全增强、连接管理及性能适配等多个维度综合考虑。以下是分阶段的详细优化策略&#xff1a;一、基础环境准备确保使用最新稳定版 Druid&#xff08;截至 2024 年推荐 1.2.38&am…

【Big Data】Apache Kafka 分布式流处理平台的实时处理实践与洞察

目录 一、Apache Kafka是什么 二、Kafka的诞生背景 三、Kafka的架构设计 四、Kafka解决的技术问题 五、Kafka的关键特性 六、Kafka与其他消息队列系统的对比 七、Kafka的工作原理 八、Kafka的部署与使用方法 1. 集群部署 2. 生产者与消费者配置 3. 安全配置 4. 监控…

23种设计模式——装饰器模式(Decorator Pattern)详解

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a;设计模式 ✨特色专栏&#xff1a;知识分享 &#x…

《sklearn机器学习——聚类性能指标》Davies-Bouldin Index (戴维斯-博尔丁指数)

Davies-Bouldin Index (戴维斯-博尔丁指数)简介 概念与定义 Davies-Bouldin Index是由David L. Davies和Donald W. Bouldin于1979年提出的一种用于评估聚类算法效果的内部指标。它通过计算每个簇内数据点之间的相似性和不同簇中心点的距离来衡量聚类结果的质量。DBI的值越低&am…

QT的学习(一)

前言&#xff1a;距离上一次摸QT已经快10年了&#xff0c;时光匆匆&#xff0c;现在已经到6.9版本了 一、安装QT 1.1、下载链接 https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/ 这是国内镜像&#xff0c;比官网快很多了&#xff0c;官网那个…

亚洲数字能源独角兽的 “安全密码”:Parasoft为星星充电筑牢软件防线

当你在充电桩前等待爱车满电时&#xff0c;是否想过&#xff1a;这看似简单的充电过程&#xff0c;背后藏着多少软件代码的精密协作&#xff1f;作为亚洲数字能源领域的头部企业&#xff0c;星星充电用 “移动能源网” 连接着千万用户与新能源世界&#xff0c;而支撑这一切的&a…

安装Codex(需要用npm)

查看已经安装的包 npm list -g --depth0 npm uninstall -g anthropic-ai/claude-code 如果要卸载什么东西 安装Codex &#xff1a;npm i -g openai/codex https://openai.com/zh-Hant/codex/ 之后登录gpt账号&#xff0c;完成后就是下面的样子

HarmonyOS 开发学习分享:从入门到认证的完整路径

HarmonyOS 开发学习分享&#xff1a;从入门到认证的完整路径 大家好&#xff01;我是赵老师&#xff0c;一个深耕鸿蒙生态的开发者。最近刚通过鸿蒙生态赋能资源丰富度建设活动的讲师认证&#xff0c;想和大家分享一下 HarmonyOS 开发的学习心得和认证经验。 我的鸿蒙开发经历作…

使用Spring Boot DevTools快速重启功能

背景 在Spring Boot项目中&#xff0c;修改一些简单的代码后&#xff0c;每次手动终止并启动整个项目比较繁琐且消耗时间。Spring Boot DevTools 提供了开发时的热重启功能&#xff0c;使得在开发过程中修改代码后可以快速生效&#xff0c;而无需手动重启整个应用&#xff0c;可…

7.4Element Plus 分页与表格组件

el-pagination el-table 这两个组件是后台管理系统中最常用的数据展示与交互组合&#xff0c;通常配合使用实现 分页加载、排序、筛选、操作 等功能。一、分页组件 el-pagination用于控制大量数据的分页展示。✅ 基本结构<el-paginationv-model:current-page"currentPa…

搭建机器学习模型的数据管道架构方案

本篇文章Designing Data Pipeline Architectures for Machine Learning Models适合对数据管道架构感兴趣的读者&#xff0c;亮点在于详细解析了传统数据仓库、云原生数据湖和现代湖仓这三种架构&#xff0c;帮助理解如何将原始数据转化为可操作的预测。文中还强调了不同架构的优…

GitHub 热榜项目 - 日榜(2025-09-06)

GitHub 热榜项目 - 日榜(2025-09-06) 生成于&#xff1a;2025-09-06 统计摘要 共发现热门项目&#xff1a;15 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜显示AI自动化与安全运维为核心趋势。Bytebot、EvolutionAPI等AI代理项目凸显自然语言交互和容器化…