鱼虾创业网

鱼虾创业网
网络营销引流推广和网络创业

Java实现RS485串口通信,SpringBoot整合WebSocket实现前后端互推消息

前言

  前段时间赶项目的过程中,遇到一个调用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只是抛砖引玉,有相关开发需求的童鞋可以看一看,参考一下大概的思路。

#感谢您访问本站#

#本文转载自互联网,若侵权,请联系删除,谢谢!#qq.com#

推荐阅读:

批发各种QQ太阳号

快排SEO整站优化:百度搜狗360关键词快速上排名,7天上首页!

趣闲赚APP:最受欢迎的手机做任务体验赚钱平台

怎么玩游戏赚钱?什么游戏最赚钱?正规的玩游戏赚现金教程!

支付宝红包可以每天免费领取,最高99元!附支付宝红包提现方法!

优质原创软文代写、高质量原创文章代写服务

网上赚钱项目:高佣联盟优惠券分享赚钱项目,月入1万+(网赚项目)

投稿中心

分享到: +More

加入VIP闷声赚大钱,轻松月赚1000,1万,100000+,甚至更多!点击查看!

鱼虾网SEO联系QQ/微信:68587353

添加好友时,记得做好备注,否则不通过

发迹创业网,关注创业、营销、自媒体和站长

扫码立即关注我们!

评论 沙了个发

换个身份

取消评论