前言

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

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

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

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

准备工作

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

  硬件:

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

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

  声光报警器(12V);

  12V电源转换器;

  电线若干;

  驱动:

  USB串口转换驱动;

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

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

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

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

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

开发实现

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

        <dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
</dependency>

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

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

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

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

  4、接收返回的信息;

  5、关闭端口连接。

  代码如下:

package com.XXX.utils;

import com.databus.Log;
import gnu.io.*; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; public class RS485Demo extends Thread implements SerialPortEventListener {
//单例模式提供连接串口的对象
private static RS485Demo getInstance(){
if (cRead == null){
synchronized (RS485Demo.class) {
if (cRead == null) {
cRead = new RS485Demo();
// 启动线程来处理收到的数据
cRead.start();
}
}
}
return cRead;
}
// 封装十六进制的打开、关闭命令
private static final List<byte[]> onOrderList = Arrays.asList(
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},
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},
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},
new byte[]{0x01, 0x05, 0x00, 0x06, (byte) 0xFF, 0x00, (byte) 0x6C, 0x3B}, new byte[]{0x01, 0x05, 0x00, 0x07, (byte) 0xFF, 0x00, 0x3D, (byte)0xFB});
private static final List<byte[]> offOrderList = Arrays.asList(
new byte[]{0x01, 0x05, 0x00, 0x00, 0x00, 0x00, (byte) 0xCD, (byte)0xCA},new byte[]{0x01, 0x05, 0x00, 0x01, 0x00, 0x00, (byte) 0x9C, (byte)0x0A},
new byte[]{0x01, 0x05, 0x00, 0x02, 0x00, 0x00, (byte) 0x6C, (byte)0x0A},new byte[]{0x01, 0x05, 0x00, 0x03, 0x00, 0x00, (byte) 0x3D, (byte)0xCA},
new byte[]{0x01, 0x05, 0x00, 0x04, 0x00, 0x00, (byte) 0x8C, (byte)0x0B},new byte[]{0x01, 0x05, 0x00, 0x05, 0x00, 0x00, (byte) 0xDD, (byte)0xCB},
new byte[]{0x01, 0x05, 0x00, 0x06, 0x00, 0x00, (byte) 0x2D, (byte)0xCB},new byte[]{0x01, 0x05, 0x00, 0x07, 0x00, 0x00, (byte) 0x7C, (byte)0x0B}); // 监听器,这里独立开辟一个线程监听串口数据
// 串口通信管理类
static CommPortIdentifier portId;
static RS485Demo cRead = null;
//USB在主机上的通信端口名称,如:COM1、COM2等
static String COMNUM = ""; static Enumeration<?> portList;
InputStream inputStream; // 从串口来的输入流
static OutputStream outputStream;// 向串口输出的流
static SerialPort serialPort; // 串口的引用
// 堵塞队列用来存放读到的数据
private BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>(); /**
* SerialPort EventListene 的方法,持续监听端口上是否有数据流
*/
public void serialEvent(SerialPortEvent event) { switch (event.getEventType()) {
case SerialPortEvent.BI:
case SerialPortEvent.OE:
case SerialPortEvent.FE:
case SerialPortEvent.PE:
case SerialPortEvent.CD:
case SerialPortEvent.CTS:
case SerialPortEvent.DSR:
case SerialPortEvent.RI:
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
break;
case SerialPortEvent.DATA_AVAILABLE:// 当有可用数据时读取数据
byte[] readBuffer = null;
int availableBytes = 0;
try {
availableBytes = inputStream.available();
while (availableBytes > 0) {
readBuffer = RS485Demo.readFromPort(serialPort);
String needData = printHexString(readBuffer);
System.out.println(new Date() + "真实收到的数据为:-----" + needData);
availableBytes = inputStream.available();
msgQueue.add(needData);
}
} catch (IOException e) {
}
default:
break;
}
} /**
* 从串口读取数据
*
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
*/
public static byte[] readFromPort(SerialPort serialPort) {
InputStream in = null;
byte[] bytes = {};
try {
in = serialPort.getInputStream();
// 缓冲区大小为一个字节
byte[] readBuffer = new byte[1];
int bytesNum = in.read(readBuffer);
while (bytesNum > 0) {
bytes = concat(bytes, readBuffer);
bytesNum = in.read(readBuffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
in = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
} /**
* 通过程序打开COM串口,设置监听器以及相关的参数
* @return 返回1 表示端口打开成功,返回 0表示端口打开失败
*/
public int startComPort() {
// 通过串口通信管理类获得当前连接上的串口列表
try {
Log.info("开始获取串口。。。");
portList = CommPortIdentifier.getPortIdentifiers();
Log.info("获取串口。。。" + portList);
Log.info("获取串口结果。。。" + portList.hasMoreElements()); while (portList.hasMoreElements()) {
// 获取相应串口对象
Log.info(portList.nextElement());
portId = (CommPortIdentifier) portList.nextElement(); System.out.println("设备类型:--->" + portId.getPortType());
System.out.println("设备名称:---->" + portId.getName());
// 判断端口类型是否为串口
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
// 判断如果COM4串口存在,就打开该串口
// if (portId.getName().equals(portId.getName())) {
if (portId.getName().equals(COMNUM)) {
try {
// 打开串口名字为COM_4(名字任意),延迟为1000毫秒
serialPort = (SerialPort) portId.open(portId.getName(), 1000); } catch (PortInUseException e) {
System.out.println("打开端口失败!");
e.printStackTrace();
return 0;
}
// 设置当前串口的输入输出流
try {
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
return 0;
}
// 给当前串口添加一个监听器,serialEvent方法监听串口返回的数据
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {
e.printStackTrace();
return 0;
}
// 设置监听器生效,即:当有数据时通知
serialPort.notifyOnDataAvailable(true); // 设置串口的一些读写参数
try {
// 比特率、数据位、停止位、奇偶校验位
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
return 0;
}
return 1;
}
}
}
}catch (Exception e){
e.printStackTrace();
Log.info(e);
return 0;
}
return 0;
} @Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println("--------------任务处理线程运行了--------------");
while (true) {
// 如果堵塞队列中存在数据就将其输出
try {
if (msgQueue.size() > 0) {
String vo = msgQueue.peek();
String vos[] = vo.split(" ", -1);
//根据返回数据可以做相应的业务逻辑操作
// getData(vos);
// sendOrder();
msgQueue.take();
}
}catch (Exception e){
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
} // 16转10计算
public long getNum(String num1, String num2) {
long value = Long.parseLong(num1, 16) * 256 + Long.parseLong(num2, 16);
return value;
} // 字节数组转字符串
private String printHexString(byte[] b) {
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sbf.append(hex.toUpperCase() + " ");
}
return sbf.toString().trim();
} /**
* 合并数组
*
* @param firstArray 第一个数组
* @param secondArray 第二个数组
* @return 合并后的数组
*/
public static byte[] concat(byte[] firstArray, byte[] secondArray) {
if (firstArray == null || secondArray == null) {
if (firstArray != null)
return firstArray;
if (secondArray != null)
return secondArray;
return null;
}
byte[] bytes = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
return bytes;
} //num:偶数启动报警器,奇数关闭报警器
//commandInfo:偶数打开,奇数关闭;channel:继电器通道;comNum:串口设备通信名称
public static void startRS485(int commandInfo,int channel,String comNum) {
try {
if(cRead == null){
cRead = getInstance();
}
if (!COMNUM.equals(comNum) && null != serialPort){
serialPort.close();
COMNUM = comNum;
}
int i = 1;
if (serialPort == null){
COMNUM = comNum;
//打开串口通道并连接
i = cRead.startComPort();
}
if (i == 1){
Log.info("串口连接成功");
try {
//根据提供的文档给出的发送命令,发送16进制数据给仪器
byte[] b;
if (commandInfo % 2 == 0) {
b = onOrderList.get(channel);
}else{
b = offOrderList.get(channel);
}
System.out.println("发送的数据:" + b);
System.out.println("发出字节数:" + b.length);
outputStream.write(b);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//每次调用完以后关闭串口通道
if (null != cRead){
if (null != serialPort){
serialPort.close();
serialPort = null;
}
cRead.interrupt();
cRead = null;
}
}
}else{
Log.info("串口连接失败");
return;
}
}catch (Exception e){
e.printStackTrace();
Log.info("串口连接失败"); }
} public static void main(String[] args) {
//打开通道1的电路,对应设备名称COM3
startRS485(0,1,"COM3");
}
}

  代码比较繁杂,需要有点耐心才能完全了解,大家可以从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. 【Alpha冲刺阶段】Day 7

    [Alpha冲刺阶段]Scrum Meeting Daily7 1.会议简述 会议开展时间 2020/5/28   8:00-8:30 PM 会议基本内容摘要 讨论合并测试问题 参与讨论人员 项目全体 ...

  2. 题解-CF429C Guess the Tree

    题面 CF429C Guess the Tree 给一个长度为 \(n\) 的数组 \(a_i\),问是否有一棵树,每个节点要么是叶子要么至少有两个儿子,而且 \(i\) 号点的子树大小是 \(a_i ...

  3. qq 表情库

    ![/qq](https://cdn.luogu.com.cn/upload/pic/62224.png) ![/cy](https://cdn.luogu.com.cn/upload/pic/622 ...

  4. C++ 虚函数表与多态 —— 虚函数表的内存布局

       C++面试经常会被问的问题就是多态原理.如果对C++面向对象本质理解不是特别好,问到这里就会崩. 下面从基本到原理,详细说说多态的实现:虚函数 & 虚函数表.   1. 多态的本质: 形 ...

  5. Python轻松入门到项目实战-实用教程

    本课程完全基于Python3讲解,针对广大的Python爱好者与同学录制.通过本课程的学习,可以让同学们在学习Python的过程中少走弯路.整个课程以实例教学为核心,通过对大量丰富的经典实例的讲解.让 ...

  6. JavaSE04-Switch&循环语句

    1.Switch 格式: 1 switch (表达式) { 2 case 1: 3 语句体1; 4 break; 5 case 2: 6 语句体2; 7 break; 8 ... 9 default: ...

  7. Spark Connector Reader 原理与实践

    本文主要讲述如何利用 Spark Connector 进行 Nebula Graph 数据的读取. Spark Connector 简介 Spark Connector 是一个 Spark 的数据连接 ...

  8. [日常摸鱼]bzoj2038[2009国家集训队]小Z的袜子-莫队算法

    今天来学了下莫队-这题应该就是这个算法的出处了 一篇别人的blog:https://www.cnblogs.com/Paul-Guderian/p/6933799.html 题意:一个序列,$m$次询 ...

  9. python pip命令的安装与实验安装scrapy

    大家在使用python时候,很多时候导入模块都会发现该模块不存在,那么我们就需要下载安装,可是有时候安装会出现各种问题,大家回去请教别人,大部分程序员会回答你:pip install 什么等,可是你p ...

  10. 时间盲注poc编写

    当测试注入漏洞时,页面没有返还结果,连报错都没有时,可以考虑延时. 比如这条语句 ?type=1 and if(length(database())=%d,sleep(5),1) 如果这条语句被服务器 ...