Java串口通讯 基于Java的串口通讯demo

自认为更好的方法

https://blog.csdn.net/qq_34775102/article/details/107212822

 

效果图

1.导入RXTXcomm.jar

链接:https://pan.baidu.com/s/1US0Re7wkzsp_v-f4M-Vhag 
提取码:bqv9

找到正在用的jdk

复制到该文件夹

 

 

 

2.代码

package com.ruoyi.web.controller.sports;

import com.ruoyi.common.config.ServerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 串口测试
 * 
 * @author niesui
 */
@Controller
@RequestMapping("/sports")
public class SportsController
{
    private static final Logger log = LoggerFactory.getLogger(SportsController.class);

    @Autowired
    private ServerConfig serverConfig;

    /**
     * 串口连接
     * @param sportsName 串口名
     * @return  Map<String, Object>
     */
    @RequestMapping(value="/connect")
    @ResponseBody
    public Map<String, Object> connect(@RequestParam(value = "sportsName", required = true) String sportsName,
                                       @RequestParam(value = "content", required = true) String content){
        Map<String,Object> map  =new HashMap<String,Object>();
        if(sportsName!=null){
            ContinueRead cRead = new ContinueRead();
            int i = cRead.startComPort(sportsName);
            if (i == 1) {
                // 启动线程来处理收到的数据
                cRead.start();
                try {
                    System.out.println("发出字节数:" + content.getBytes("gbk").length);
                    ContinueRead.outputStream.write(content.getBytes("gbk"), 0,
                            content.getBytes("gbk").length);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    map.clear();
                    map.put("code", 0);
                    map.put("errorMsg", "发送失败");
                    return map;
                }
                map.clear();
                map.put("code", 1);
                map.put("results", content);
                return map;
            } else {
                map.clear();
                map.put("code", 0);
                map.put("errorMsg", "运行失败");
                return map;
            }
        }else {
            map.clear();
            map.put("code", 0);
            map.put("errorMsg", "未选择串口");
            return map;
        }
    }
    /**
     *
     * @param content
     * @return  Map<String, Object>
     */
    @RequestMapping(value="/send")
    @ResponseBody
    public Map<String, Object> send(@RequestParam(value = "content", required = true) String content){
        Map<String,Object> map  =new HashMap<String,Object>();
        if(content!=null){
            try {
               ContinueRead.outputStream.write(content.getBytes("gbk"), 0, content.getBytes("gbk").length);
               map.clear();
               map.put("code", 1);
               map.put("results", content);
               return map;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                map.clear();
                map.put("code", 0);
                map.put("errorMsg", "发送失败");
                return map;
            }
        } else {
             map.clear();
             map.put("code", 0);
             map.put("errorMsg", "运行失败");
             return map;
        }
    }
}
package com.ruoyi.web.controller.sports;


import java.io.*;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import gnu.io.*;

public class ContinueRead extends Thread implements SerialPortEventListener { // SerialPortEventListener
    // 监听器,我的理解是独立开辟一个线程监听串口数据
    static CommPortIdentifier portId; // 串口通信管理类
    static Enumeration<?> portList; // 有效连接上的端口的枚举
    InputStream inputStream; // 从串口来的输入流
    static OutputStream outputStream;// 向串口输出的流
    static SerialPort serialPort; // 串口的引用
    // 堵塞队列用来存放读到的数据
    private BlockingQueue<String> msgQueue = new LinkedBlockingQueue<String>();

    @Override
    /**
     * 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 = new byte[20];
                try {
                    int numBytes = -1;
                    while (inputStream.available() > 0) {
                        numBytes = inputStream.read(readBuffer);

                        if (numBytes > 0) {
                            msgQueue.add(new Date() + "真实收到的数据为:-----"
                                    + new String(readBuffer));
                            try {
                                //WebSocket处理串口发过来的数据发到前端ID:10001
                                WebSocket.sendInfo(new String(readBuffer),"10001");
                            } catch (Exception e) {
                                System.out.println(e);
                            }
                            readBuffer = new byte[20];// 重新构造缓冲对象,否则有可能会影响接下来接收的数据
                        } else {
                            msgQueue.add("额------没有读到数据");
                        }
                    }
                } catch (IOException e) {
                }
                break;
        }
    }

    /**
     *
     * 通过程序打开COM串口,设置监听器以及相关的参数
     *
     * @return 返回1 表示端口打开成功,返回 0表示端口打开失败
     */
    public int startComPort(String ss) {
        // 通过串口通信管理类获得当前连接上的串口列表
        portList = CommPortIdentifier.getPortIdentifiers();

        while (portList.hasMoreElements()) {

            // 获取相应串口对象
            portId = (CommPortIdentifier) portList.nextElement();

            System.out.println("设备类型:--->" + portId.getPortType());
            System.out.println("设备名称:---->" + portId.getName());
            // 判断端口类型是否为串口
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                // 判断如果COM4串口存在,就打开该串口
                    try {
                        // 打开串口名字为COM_4(名字任意),延迟为2毫秒
                        serialPort = (SerialPort) portId.open( ss, 2000);

                    } catch (PortInUseException e) {
                        e.printStackTrace();
                        return 0;
                    }
                    // 设置当前串口的输入输出流
                    try {
                        inputStream = serialPort.getInputStream();
                        outputStream = serialPort.getOutputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                        return 0;
                    }
                    // 给当前串口添加一个监听器
                    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;
                }
        }
        return 0;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            System.out.println("--------------任务处理线程运行了--------------");
            while (true) {
                // 如果堵塞队列中存在数据就将其输出
                if (msgQueue.size() > 0) {
                    System.out.println(msgQueue.take());
                }
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
package com.ruoyi.web.controller.sports;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;


@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocket {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    private String sid="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
        this.sid=sid;
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            System.out.println("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到来自窗口"+sid+"的信息:"+message);
        //群发消息
        for (WebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        System.out.println("推送消息到窗口"+sid+",推送内容:"+message);
        for (WebSocket item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的,为null则全部推送
                if(sid==null) {
                    item.sendMessage(message);
                }else if(item.sid.equals(sid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocket.onlineCount--;
    }
}

package com.ruoyi.web.controller.sports;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 开启WebSocket支持
 * @author ns
 */
@Configuration
public class MyWbeSocketConfig {

    @Bean
    public ServerEndpointExporter  serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

HTML

<!DOCTYPE html>
<html lang="zh">
<head>
    <!--      框架自带-->
	<th:block th:include="include :: header('串口通讯')" />
    <!--      框架自带-->
</head>
<body class="gray-bg">
      <div class="wrapper wrapper-content animated fadeInRight">
        <div class="row">

            
            <div class="col-sm-6">
                <div class="ibox float-e-margins">
                    <div class="ibox-title">
                        <h5>串口测试<small>QQ:873103462</small></h5>
                    </div>  
                    <div class="ibox-content">
                        <div class="form-group">
                            <div class="col-sm-8">
                                <select class="form-control" id="cktx">
                                    <option value="">--请选择串口--</option>
                                    <option value="COM4">COM4</option>
                                    <option value="COM1">COM1</option>
                                    <option value="COM2">COM2</option>
                                    <option value="COM3">COM3</option>
                                    <option value="COM5">COM5</option>
                                    <option value="COM6">COM6</option>
                            </select>
                            </div>
                            <button type="button" class="btn btn-w-m btn-primary" onclick="lianjie()">连接</button>
                            <span id="zt"></span>
                        </div>
                        <div class="form-group">
                            <div id="ccomment" name="comment" class="form-control" style="height: 300px;overflow-y:scroll;">

                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-sm-8">
                                <input class="form-control" type="text"  id="neirong">
                            </div>
                            <button type="button" class="btn btn-w-m btn-primary" onclick="fasong()">发送</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
   <!--      框架自带-->
    <th:block th:include="include :: footer" />
    <th:block th:include="include :: bootstrap-suggest-js" />
    <th:block th:include="include :: bootstrap-typeahead-js" />
      <!--      框架自带-->
    <script type="text/javascript">
        var rootPath="localhost:8080";
        var socket;
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else{
            var cid=$("#cid").val();
            console.log("您的浏览器支持WebSocket");

            //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
            socket = new WebSocket("ws://"+rootPath+"/websocket/10001");
            //打开事件
            socket.onopen = function() {
                console.log("Socket 已打开");
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                console.log(msg.data);
                $("#ccomment").append("<h5>"+dates()+"     "+msg.data+"</h5>");
                //发现消息进入    开始处理前端触发逻辑
            };
            //关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //发生了错误事件
            socket.onerror = function() {
                console.log("Socket发生了错误");
                //此时可以尝试刷新页面
            }
        }

    function lianjie() {
            console.log($("#cktx").val());
        $.ajax({
            url:"http://"+rootPath+'/sports/connect',
            type:"POST",
            async:true,
            data:{
                sportsName:$("#cktx").val(),
                content:"连接"
            },complete:function (xhr,textStatus) {
                var data = JSON.parse(xhr.responseText);
                console.log(data);
                if (data.code + "" == "1") {
                    $("#ccomment").append("<h5 style='color:#5cb85c'>"+dates()+"     "+data.results+"  </h5>");

                }else {
                    $("#ccomment").append("<h5 style='color: red'>"+dates()+"     "+data.results+"  </h5>");
                }
            }
        })
    }

        function dates() {
            var time = new Date();   // 程序计时的月从0开始取值后+1
            var m = time.getMonth() + 1;
            var t = time.getFullYear() + "-" + m + "-"
                + time.getDate() + " " + time.getHours() + ":"
                + time.getMinutes() + ":" + time.getSeconds();
            return t;
        }
        function fasong() {
            $.ajax({
                url:"http://"+rootPath+'/sports/send',
                type:"POST",
                async:true,
                data:{
                    content:$("#neirong").val()
                },complete:function (xhr,textStatus) {
                    var data = JSON.parse(xhr.responseText);
                    console.log(data);
                    if (data.code + "" == "1") {
                        $("#ccomment").append("<h5 style='color:#5cb85c'>"+dates()+"     "+data.results+"  </h5>");

                    }else {
                        $("#ccomment").append("<h5 style='color: red'>"+dates()+"     "+data.results+"  </h5>");
                    }
                }
            })
        }
    </script>
</body>
</html>

串口测试工具

链接:https://pan.baidu.com/s/1zbuxhZhJi19XmDi1P1jQHQ 
提取码:p7sf 


虚拟串口驱动

链接:https://pan.baidu.com/s/1L2Cr7YdZzDJHqIOaZ6X_cA 
提取码:3aos 
 

特别鸣谢 https://www.cnblogs.com/swp520lmg/articles/8636732.html (部分模块)

源码 https://download.csdn.net/download/qq_34775102/12437929

 

posted @ 2022-07-16 13:52  深山藏古客  阅读(629)  评论(0编辑  收藏  举报