Java工業通信實戰(三):Modbus RTU串口通信實現

想象一下,你要和一臺工業設備"對話",比如詢問溫度傳感器"現在多少度?“或者告訴電機"轉快一點”。
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代理項目凸顯自然語言交互和容器化…