Java实现RS485串口通信
前言
前段时间赶项目的过程中,遇到一个调用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串口通信的更多相关文章
- Java实现RS485串口通信,发送和接收数据进行解析
最近项目有一个空气检测仪,需要得到空气检测仪的实时数据,保存到数据库当中.根据了解得到,硬件是通过rs485进行串口通讯的,需要发送16进制命令给仪器,然后通过轮询来得到数据. 需要先要下载RXTX的 ...
- java下的串口通信-RXTX
关于java实现的串口通信,使用的是开源项目RXTX,之前sun公司也有JCL项目,不过已经很久没有更新了,RXTX项目地址:点击打开,但是两个项目的API用法是一样的,只是导入的包不一样而已.简单的 ...
- Python编程实现USB转RS485串口通信
---作者吴疆,未经允许,严禁转载,违权必究--- ---欢迎指正,需要源码和文件可站内私信联系--- -----------点击此处链接至博客园原文----------- 功能说明:Python编程 ...
- 使用Java实现简单串口通信
最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar ...
- java可用与串口通信的一些库
java原生对串口的支持只有javax.comm,javax.comm比较老了,而且不支持64位系统,我在看jlibmodbus(一个java实现的modbus协议栈)的时候发现了几个可供使用的jav ...
- Java 串口通信
在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...
- java 串口通信 代码
下面是我自己实现的串口接收的类,串口发送比较简单,就直接发送就可以了.下面的这个类可以直接使用. package com.boomdts.weather_monitor.util; import ja ...
- Java串口通信具体解释
序言 说到开源,恐怕非常少有人不挑大指称赞.学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜.然而开源软 ...
- Java实现串口通信的小样例
用Java实现串口通信(windows系统下),须要用到sun提供的串口包 javacomm20-win32.zip.当中要用到三个文件,配置例如以下: 1.comm.jar放置到 JAVA_HOME ...
随机推荐
- DFS,BFS 练习(深搜,广搜,图,leetcode)
https://leetcode-cn.com/problems/route-between-nodes-lcci/ 节点间通路.给定有向图,设计一个算法,找出两个节点之间是否存在一条路径. 示例1: ...
- 学习JUC源码(1)——AQS同步队列(源码分析结合图文理解)
前言 最近结合书籍<Java并发编程艺术>一直在看AQS的源码,发现AQS核心就是:利用内置的FIFO双向队列结构来实现线程排队获取int变量的同步状态,以此奠定了很多并发包中大部分实现基 ...
- 笔记-AHOI2013 差异
AHOI2013 差异 方法1:SA 先板个后缀数组(带 \(height\) 不带 \(st\) 表),用单调队列递推每个后缀 \(sa_i\) 对答案的贡献,求和,用定值减之. #include ...
- 基于Fisco-Bcos的区块链智能合约-业务数据上链SDK实现
合约的编写 基于springboot : https://github.com/FISCO-BCOS/spring-boot-starter pragma solidity ^0.4.24; cont ...
- 数据结构,哈希表hash设计实验
数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...
- docker(专业版) 安装过程报错
1.安装docker Desktop时遇到的错误 1.1安装Docker Desktop报错:WSL 2 installation is incomplete 解决: # 更新版本 https://b ...
- Springboot mini - Solon详解(八)- Solon的缓存框架使用和定制
Springboot min -Solon 详解系列文章: Springboot mini - Solon详解(一)- 快速入门 Springboot mini - Solon详解(二)- Solon ...
- dataframe的一些用法
pandas中Dataframe的一些用法 pandas读取excel文件 pd.read_excel 前提是安装xlrd库 dataframe,numpy,list之间的互相转换 dataframe ...
- Consul的使用
Consul的使用 生产部署中,Consul安装在要注册服务的每个节点上.Consul有两种运行模式:客户端和服务器端,每个Consul数据中心必须至少有一个服务器,负责维护Consul状态,为了 ...
- django配置-mysql数据库相关配置
Django3版本之前的配置 1. Django默认配置的数据库是sqlite 2. 使用mysql数据库需要下载的包 pip3 install PyMySQL 3. Django配置PyMySQL ...