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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?