前言

  前段时间赶项目的过程中,遇到一个调用RS485串口通信的需求,赶完项目因为楼主处理私事,没来得及完成文章的更新,现在终于可以整理一下当时的demo,记录下来。

  首先说一下大概需求:这个项目是机器视觉方面的,AI算法通过摄像头视频流检测画面中的目标事件,比如:火焰、烟雾、人员离岗、吸烟、打手机、车辆超速等,检测到目标事件后上传检测结果到后台系统,

后台系统存储检测结果并推送结果到前端,这里用的是SpringBoot整合WebSocket实现前后端互推消息,感兴趣的同学可以看一看,大家多交流。然后就是今天的主题,系统在推送检测结果到前端的同时,需要触发

声光报警器,现有条件就是系统调用支持RS485串口的继电器控制电路,进而达到打开和关闭报警器的目的。

准备工作

  说了这么多可能没什么具体的概念,下面先列出需要的硬件设备及准备工作:

  硬件:

  USB串口转换器(现在很多主机和笔记本已经没有485串口的接口了,转换器淘宝可以买到);

  RS485继电器(12V,继电器模块有8个通道,模块的寄存器有对应8个通道的命令);

  声光报警器(12V);

  12V电源转换器;

  电线若干;

  驱动:

  USB串口转换驱动;

  看了这些硬件,感觉楼主是电工是吧?没错,楼主确实是自己摸索着连接的,下面上图:

  线路如何接不是本文的重点,用12V的硬件就是因为安全,楼主可以大胆尝试。。。

  接通硬件设备后,在系统中查看串口名称,如下图,可以看到通信端口名称是COM1,其实电脑上每个硬件接口都是有固定名称的,USB插在不同的USB接口上,系统读取到的通信端口名称就是对应接口的名称,这里

的端口名称要记下来,后面编码要用到。

  然后是搬砖前的最后一步准备工作:安装驱动。楼主的USB串口转换器是在淘宝上买的,商家提供驱动,在电脑上正常安装驱动即可。

开发实现

  首先需要引入rxtx的jar包,Java实现串口通信的依赖,如下:

  1. <dependency>
  2. <groupId>org.rxtx</groupId>
  3. <artifactId>rxtx</artifactId>
  4. <version>2.1.7</version>
  5. </dependency>

  引入jar包后,就可以搬砖了,大概思路如下:

  1、获取到与串口通信的对象;

  2、打开对应串口的端口并建立连接;

  3、获取对应通道的命令并发送;

  4、接收返回的信息;

  5、关闭端口连接。

  代码如下:

  1. package com.XXX.utils;
  2.  
  3. import com.databus.Log;
  4. import gnu.io.*;
  5.  
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.util.*;
  10. import java.util.concurrent.BlockingQueue;
  11. import java.util.concurrent.LinkedBlockingQueue;
  12.  
  13. public class RS485Demo extends Thread implements SerialPortEventListener {
  14. //单例模式提供连接串口的对象
  15. private static RS485Demo getInstance(){
  16. if (cRead == null){
  17. synchronized (RS485Demo.class) {
  18. if (cRead == null) {
  19. cRead = new RS485Demo();
  20. // 启动线程来处理收到的数据
  21. cRead.start();
  22. }
  23. }
  24. }
  25. return cRead;
  26. }
  27. // 封装十六进制的打开、关闭命令
  28. private static final List<byte[]> onOrderList = Arrays.asList(
  29. new byte[]{0x01, 0x05, 0x00, 0x00, (byte) 0xFF, 0x00, (byte) 0x8C, 0x3A}, new byte[]{0x01, 0x05, 0x00, 0x01, (byte) 0xFF, 0x00, (byte) 0xDD, (byte)0xFA},
  30. new byte[]{0x01, 0x05, 0x00, 0x02, (byte) 0xFF, 0x00, (byte) 0x2D, (byte)0xFA}, new byte[]{0x01, 0x05, 0x00, 0x03, (byte) 0xFF, 0x00, (byte) 0x7C, 0x3A},
  31. new byte[]{0x01, 0x05, 0x00, 0x04, (byte) 0xFF, 0x00, (byte) 0xCD,(byte) 0xFB}, new byte[]{0x01, 0x05, 0x00, 0x05, (byte) 0xFF, 0x00, (byte) 0x9C, 0x3B},
  32. new byte[]{0x01, 0x05, 0x00, 0x06, (byte) 0xFF, 0x00, (byte) 0x6C, 0x3B}, new byte[]{0x01, 0x05, 0x00, 0x07, (byte) 0xFF, 0x00, 0x3D, (byte)0xFB});
  33. private static final List<byte[]> offOrderList = Arrays.asList(
  34. new byte[]{0x01, 0x05, 0x00, 0x00, 0x00, 0x00, (byte) 0xCD, (byte)0xCA},new byte[]{0x01, 0x05, 0x00, 0x01, 0x00, 0x00, (byte) 0x9C, (byte)0x0A},
  35. new byte[]{0x01, 0x05, 0x00, 0x02, 0x00, 0x00, (byte) 0x6C, (byte)0x0A},new byte[]{0x01, 0x05, 0x00, 0x03, 0x00, 0x00, (byte) 0x3D, (byte)0xCA},
  36. new byte[]{0x01, 0x05, 0x00, 0x04, 0x00, 0x00, (byte) 0x8C, (byte)0x0B},new byte[]{0x01, 0x05, 0x00, 0x05, 0x00, 0x00, (byte) 0xDD, (byte)0xCB},
  37. new byte[]{0x01, 0x05, 0x00, 0x06, 0x00, 0x00, (byte) 0x2D, (byte)0xCB},new byte[]{0x01, 0x05, 0x00, 0x07, 0x00, 0x00, (byte) 0x7C, (byte)0x0B});
  38.  
  39. // 监听器,这里独立开辟一个线程监听串口数据
  40. // 串口通信管理类
  41. static CommPortIdentifier portId;
  42. static RS485Demo cRead = null;
  43. //USB在主机上的通信端口名称,如:COM1、COM2等
  44. static String COMNUM = "";
  45.  
  46. static Enumeration<?> portList;
  47. InputStream inputStream; // 从串口来的输入流
  48. static OutputStream outputStream;// 向串口输出的流
  49. static SerialPort serialPort; // 串口的引用
  50. // 堵塞队列用来存放读到的数据
  51. private BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>();
  52.  
  53. /**
  54. * SerialPort EventListene 的方法,持续监听端口上是否有数据流
  55. */
  56. public void serialEvent(SerialPortEvent event) {
  57.  
  58. switch (event.getEventType()) {
  59. case SerialPortEvent.BI:
  60. case SerialPortEvent.OE:
  61. case SerialPortEvent.FE:
  62. case SerialPortEvent.PE:
  63. case SerialPortEvent.CD:
  64. case SerialPortEvent.CTS:
  65. case SerialPortEvent.DSR:
  66. case SerialPortEvent.RI:
  67. case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
  68. break;
  69. case SerialPortEvent.DATA_AVAILABLE:// 当有可用数据时读取数据
  70. byte[] readBuffer = null;
  71. int availableBytes = 0;
  72. try {
  73. availableBytes = inputStream.available();
  74. while (availableBytes > 0) {
  75. readBuffer = RS485Demo.readFromPort(serialPort);
  76. String needData = printHexString(readBuffer);
  77. System.out.println(new Date() + "真实收到的数据为:-----" + needData);
  78. availableBytes = inputStream.available();
  79. msgQueue.add(needData);
  80. }
  81. } catch (IOException e) {
  82. }
  83. default:
  84. break;
  85. }
  86. }
  87.  
  88. /**
  89. * 从串口读取数据
  90. *
  91. * @param serialPort 当前已建立连接的SerialPort对象
  92. * @return 读取到的数据
  93. */
  94. public static byte[] readFromPort(SerialPort serialPort) {
  95. InputStream in = null;
  96. byte[] bytes = {};
  97. try {
  98. in = serialPort.getInputStream();
  99. // 缓冲区大小为一个字节
  100. byte[] readBuffer = new byte[1];
  101. int bytesNum = in.read(readBuffer);
  102. while (bytesNum > 0) {
  103. bytes = concat(bytes, readBuffer);
  104. bytesNum = in.read(readBuffer);
  105. }
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. } finally {
  109. try {
  110. if (in != null) {
  111. in.close();
  112. in = null;
  113. }
  114. } catch (IOException e) {
  115. e.printStackTrace();
  116. }
  117. }
  118. return bytes;
  119. }
  120.  
  121. /**
  122. * 通过程序打开COM串口,设置监听器以及相关的参数
  123. * @return 返回1 表示端口打开成功,返回 0表示端口打开失败
  124. */
  125. public int startComPort() {
  126. // 通过串口通信管理类获得当前连接上的串口列表
  127. try {
  128. Log.info("开始获取串口。。。");
  129. portList = CommPortIdentifier.getPortIdentifiers();
  130. Log.info("获取串口。。。" + portList);
  131. Log.info("获取串口结果。。。" + portList.hasMoreElements());
  132.  
  133. while (portList.hasMoreElements()) {
  134. // 获取相应串口对象
  135. Log.info(portList.nextElement());
  136. portId = (CommPortIdentifier) portList.nextElement();
  137.  
  138. System.out.println("设备类型:--->" + portId.getPortType());
  139. System.out.println("设备名称:---->" + portId.getName());
  140. // 判断端口类型是否为串口
  141. if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
  142. // 判断如果COM4串口存在,就打开该串口
  143. // if (portId.getName().equals(portId.getName())) {
  144. if (portId.getName().equals(COMNUM)) {
  145. try {
  146. // 打开串口名字为COM_4(名字任意),延迟为1000毫秒
  147. serialPort = (SerialPort) portId.open(portId.getName(), 1000);
  148.  
  149. } catch (PortInUseException e) {
  150. System.out.println("打开端口失败!");
  151. e.printStackTrace();
  152. return 0;
  153. }
  154. // 设置当前串口的输入输出流
  155. try {
  156. inputStream = serialPort.getInputStream();
  157. outputStream = serialPort.getOutputStream();
  158. } catch (IOException e) {
  159. e.printStackTrace();
  160. return 0;
  161. }
  162. // 给当前串口添加一个监听器,serialEvent方法监听串口返回的数据
  163. try {
  164. serialPort.addEventListener(this);
  165. } catch (TooManyListenersException e) {
  166. e.printStackTrace();
  167. return 0;
  168. }
  169. // 设置监听器生效,即:当有数据时通知
  170. serialPort.notifyOnDataAvailable(true);
  171.  
  172. // 设置串口的一些读写参数
  173. try {
  174. // 比特率、数据位、停止位、奇偶校验位
  175. serialPort.setSerialPortParams(9600,
  176. SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
  177. SerialPort.PARITY_NONE);
  178. } catch (UnsupportedCommOperationException e) {
  179. e.printStackTrace();
  180. return 0;
  181. }
  182. return 1;
  183. }
  184. }
  185. }
  186. }catch (Exception e){
  187. e.printStackTrace();
  188. Log.info(e);
  189. return 0;
  190. }
  191. return 0;
  192. }
  193.  
  194. @Override
  195. public void run() {
  196. // TODO Auto-generated method stub
  197. try {
  198. System.out.println("--------------任务处理线程运行了--------------");
  199. while (true) {
  200. // 如果堵塞队列中存在数据就将其输出
  201. try {
  202. if (msgQueue.size() > 0) {
  203. String vo = msgQueue.peek();
  204. String vos[] = vo.split(" ", -1);
  205. //根据返回数据可以做相应的业务逻辑操作
  206. // getData(vos);
  207. // sendOrder();
  208. msgQueue.take();
  209. }
  210. }catch (Exception e){
  211. e.printStackTrace();
  212. }
  213. }
  214. } catch (Exception e) {
  215. e.printStackTrace();
  216. }
  217. }
  218.  
  219. // 16转10计算
  220. public long getNum(String num1, String num2) {
  221. long value = Long.parseLong(num1, 16) * 256 + Long.parseLong(num2, 16);
  222. return value;
  223. }
  224.  
  225. // 字节数组转字符串
  226. private String printHexString(byte[] b) {
  227. StringBuffer sbf = new StringBuffer();
  228. for (int i = 0; i < b.length; i++) {
  229. String hex = Integer.toHexString(b[i] & 0xFF);
  230. if (hex.length() == 1) {
  231. hex = '0' + hex;
  232. }
  233. sbf.append(hex.toUpperCase() + " ");
  234. }
  235. return sbf.toString().trim();
  236. }
  237.  
  238. /**
  239. * 合并数组
  240. *
  241. * @param firstArray 第一个数组
  242. * @param secondArray 第二个数组
  243. * @return 合并后的数组
  244. */
  245. public static byte[] concat(byte[] firstArray, byte[] secondArray) {
  246. if (firstArray == null || secondArray == null) {
  247. if (firstArray != null)
  248. return firstArray;
  249. if (secondArray != null)
  250. return secondArray;
  251. return null;
  252. }
  253. byte[] bytes = new byte[firstArray.length + secondArray.length];
  254. System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
  255. System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
  256. return bytes;
  257. }
  258.  
  259. //num:偶数启动报警器,奇数关闭报警器
  260. //commandInfo:偶数打开,奇数关闭;channel:继电器通道;comNum:串口设备通信名称
  261. public static void startRS485(int commandInfo,int channel,String comNum) {
  262. try {
  263. if(cRead == null){
  264. cRead = getInstance();
  265. }
  266. if (!COMNUM.equals(comNum) && null != serialPort){
  267. serialPort.close();
  268. COMNUM = comNum;
  269. }
  270. int i = 1;
  271. if (serialPort == null){
  272. COMNUM = comNum;
  273. //打开串口通道并连接
  274. i = cRead.startComPort();
  275. }
  276. if (i == 1){
  277. Log.info("串口连接成功");
  278. try {
  279. //根据提供的文档给出的发送命令,发送16进制数据给仪器
  280. byte[] b;
  281. if (commandInfo % 2 == 0) {
  282. b = onOrderList.get(channel);
  283. }else{
  284. b = offOrderList.get(channel);
  285. }
  286. System.out.println("发送的数据:" + b);
  287. System.out.println("发出字节数:" + b.length);
  288. outputStream.write(b);
  289. outputStream.flush();
  290. } catch (IOException e) {
  291. e.printStackTrace();
  292. } finally {
  293. try {
  294. if (outputStream != null) {
  295. outputStream.close();
  296. }
  297. } catch (IOException e) {
  298. e.printStackTrace();
  299. }
  300. //每次调用完以后关闭串口通道
  301. if (null != cRead){
  302. if (null != serialPort){
  303. serialPort.close();
  304. serialPort = null;
  305. }
  306. cRead.interrupt();
  307. cRead = null;
  308. }
  309. }
  310. }else{
  311. Log.info("串口连接失败");
  312. return;
  313. }
  314. }catch (Exception e){
  315. e.printStackTrace();
  316. Log.info("串口连接失败");
  317.  
  318. }
  319. }
  320.  
  321. public static void main(String[] args) {
  322. //打开通道1的电路,对应设备名称COM3
  323. startRS485(0,1,"COM3");
  324. }
  325. }

  代码比较繁杂,需要有点耐心才能完全了解,大家可以从startRS485()函数作为切入点阅读代码。当然,这个demo只是抛砖引玉,有相关开发需求的童鞋可以看一看,参考一下大概的思路。

Java实现RS485串口通信的更多相关文章

  1. Java实现RS485串口通信,发送和接收数据进行解析

    最近项目有一个空气检测仪,需要得到空气检测仪的实时数据,保存到数据库当中.根据了解得到,硬件是通过rs485进行串口通讯的,需要发送16进制命令给仪器,然后通过轮询来得到数据. 需要先要下载RXTX的 ...

  2. java下的串口通信-RXTX

    关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...

  3. Python编程实现USB转RS485串口通信

    ---作者吴疆,未经允许,严禁转载,违权必究--- ---欢迎指正,需要源码和文件可站内私信联系--- -----------点击此处链接至博客园原文----------- 功能说明:Python编程 ...

  4. 使用Java实现简单串口通信

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar ...

  5. java可用与串口通信的一些库

    java原生对串口的支持只有javax.comm,javax.comm比较老了,而且不支持64位系统,我在看jlibmodbus(一个java实现的modbus协议栈)的时候发现了几个可供使用的jav ...

  6. Java 串口通信

    在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...

  7. java 串口通信 代码

    下面是我自己实现的串口接收的类,串口发送比较简单,就直接发送就可以了.下面的这个类可以直接使用. package com.boomdts.weather_monitor.util; import ja ...

  8. Java串口通信具体解释

    序言 说到开源,恐怕非常少有人不挑大指称赞.学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜.然而开源软 ...

  9. Java实现串口通信的小样例

    用Java实现串口通信(windows系统下),须要用到sun提供的串口包 javacomm20-win32.zip.当中要用到三个文件,配置例如以下: 1.comm.jar放置到 JAVA_HOME ...

随机推荐

  1. DFS,BFS 练习(深搜,广搜,图,leetcode)

    https://leetcode-cn.com/problems/route-between-nodes-lcci/ 节点间通路.给定有向图,设计一个算法,找出两个节点之间是否存在一条路径. 示例1: ...

  2. 学习JUC源码(1)——AQS同步队列(源码分析结合图文理解)

    前言 最近结合书籍<Java并发编程艺术>一直在看AQS的源码,发现AQS核心就是:利用内置的FIFO双向队列结构来实现线程排队获取int变量的同步状态,以此奠定了很多并发包中大部分实现基 ...

  3. 笔记-AHOI2013 差异

    AHOI2013 差异 方法1:SA 先板个后缀数组(带 \(height\) 不带 \(st\) 表),用单调队列递推每个后缀 \(sa_i\) 对答案的贡献,求和,用定值减之. #include ...

  4. 基于Fisco-Bcos的区块链智能合约-业务数据上链SDK实现

    合约的编写 基于springboot : https://github.com/FISCO-BCOS/spring-boot-starter pragma solidity ^0.4.24; cont ...

  5. 数据结构,哈希表hash设计实验

    数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...

  6. docker(专业版) 安装过程报错

    1.安装docker Desktop时遇到的错误 1.1安装Docker Desktop报错:WSL 2 installation is incomplete 解决: # 更新版本 https://b ...

  7. Springboot mini - Solon详解(八)- Solon的缓存框架使用和定制

    Springboot min -Solon 详解系列文章: Springboot mini - Solon详解(一)- 快速入门 Springboot mini - Solon详解(二)- Solon ...

  8. dataframe的一些用法

    pandas中Dataframe的一些用法 pandas读取excel文件 pd.read_excel 前提是安装xlrd库 dataframe,numpy,list之间的互相转换 dataframe ...

  9. Consul的使用

    Consul的使用 ​ 生产部署中,Consul安装在要注册服务的每个节点上.Consul有两种运行模式:客户端和服务器端,每个Consul数据中心必须至少有一个服务器,负责维护Consul状态,为了 ...

  10. django配置-mysql数据库相关配置

    Django3版本之前的配置 1. Django默认配置的数据库是sqlite 2. 使用mysql数据库需要下载的包 pip3 install PyMySQL 3. Django配置PyMySQL ...