java-串口通讯-连接硬件

串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

上面说的,对于非专业人士来说,都是多余的话
一句话简单的说:串口通讯,一般情况下指windows电脑(或者一些安卓系统,单片机系统)和硬件进行数据交互的协议(方式)(硬件指,扫码抢,扫码盒子,刷卡机,继电器等一些硬件)。

这些硬件一般通过串口线,或者是USB线, 如果不带USB线也可以通过转换成USB线的方式,和电脑连接。

举例几个场景,一个windows 系统的柜式机器,控制开门关门。
一个windows机器的闸机,控制开门关门,或者开灯关灯
一个扫码枪,想要获取扫码数据并处理扫码获得的数据。等等各种情况
我这里主要是以 继电器 和 扫码器两种情况为例子。

场景一:windows电脑控制继电器的开关**

这种场景主要是,windows主动发送串口指令到硬件,控制硬件的状态
淘宝上买了一个继电器
在这里插入图片描述
话不多说,直接上串口通讯代码
POM 依赖包

<dependency>
		    <groupId>com.fazecast</groupId>
		    <artifactId>jSerialComm</artifactId>
		    <version>2.9.0</version>
		</dependency>
package com.hzsmk.serial.service;

import com.fazecast.jSerialComm.SerialPort;
import com.hzsmk.common.exception.BusinessException;
import com.hzsmk.common.util.RString;
 
public class SerialHander {
	
	private static final byte[] open = new byte[] {(byte) 0xA0,0x01,0x01,(byte) 0xA2};
	
	private static final byte[] close = new byte[] {(byte) 0xA0,0x01,0x00,(byte) 0xA1};
	
	//private static final byte[] query = new byte[] {(byte) 0xA0,0x01,0x05,(byte) 0xA6};
	
    private static SerialPort connectPort(String portDescription,String systemPortName) {
        // 列举所有可用的串口
        SerialPort[] commPorts = SerialPort.getCommPorts();
        for (SerialPort port : commPorts) {
        	String des = port.getPortDescription();
        	System.out.println(des);
        	if(RString.isNotBlank(portDescription) &&  !portDescription.equals(des)) {
        		//端口描述不為空,需要检验 
        		continue;
        	}
        	
        	String portname = port.getSystemPortName();
        	if(RString.isNotBlank(systemPortName) &&  !systemPortName.equals(portname)) {
        		//端口不為空,需要检验 
        		continue;
        	}
        	return port;
        }
        throw new BusinessException("未找到设备:"+portDescription+",端口"+systemPortName);
        
    }
    
    /**
     * 发送一个开关信号
     * @param portDescription
     * @param systemPortName
     * @throws InterruptedException 
     */
    public static void hand(String portDescription,String systemPortName) {
    	//获取可用端口
    	SerialPort serialPort = connectPort(portDescription, systemPortName);
        try {
        	//连接
        	if(!serialPort.isOpen()) {
        		boolean isopen = serialPort.openPort();
            	if(!isopen) {
            		//连接失败
            		 throw new BusinessException("连接设备失败,请重试,设备:"+portDescription+",端口"+systemPortName); 
            	}
        	}
        	//发送2个数据过去
            serialPort.writeBytes(open, open.length);
        	Thread.sleep(300);//随眠200毫秒 关闭
        	serialPort.writeBytes(close, close.length);
        }catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BusinessException e) {
        	throw e;
        } finally {
            // 关闭串口
            serialPort.closePort();
        }
    }
    
}

1、核心代码是,查询出已经连接的串口设备,SerialPort.getCommPorts();

2、打开串口设备:serialPort.openPort();

3、发送串口数据:serialPort.writeBytes(close, close.length);

场景2,被动接受扫码器扫码获得的数据

这种场景主要是,串口支持被动获取数据。,
要解决的问题1: 要动态监测串口设备是否已失去连接,
2:串口设备支持自动重连。

package com.hzsmk.serial.service;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.fazecast.jSerialComm.SerialPort;
import com.hzsmk.common.exception.BusinessException;
import com.hzsmk.common.util.RString;

@Service
public class ScanHander {

	private static SerialPort scanSerialPort = null;
	
	public static String scanSerialPortName = "";
	
	@Value("${serial.scanportdes}")
	String envScanPortdes;
	
	/**
	 * 每5秒检查一次
	 */
	@Scheduled(cron = "0/5 * * * * *")
	public void scheduled(){
		System.out.println("定时检查设备是否运行中");
	    if(scanSerialPort == null || !scanSerialPort.isOpen() || scanSerialPort.bytesAvailable() == -1) {
	    	//主要依赖  bytesAvailable 参数,  isopen基本无用
	    	scanSerialPortName = "";//重置信号参数
	    	   synchronized (scanSerialPortName) {
	    		   try {
	    			   if(RString.isBlank(envScanPortdes)) {
	    				   createListen("SM-2D PRODUCT USB UART","");
	    			   }else {
	    				   createListen(envScanPortdes,"");
	    			   }
	    			   
					} catch (Exception e) {
						e.printStackTrace();
					}
			}
	    }
	}
	
    private static SerialPort connectPort(String portDescription,String systemPortName) {
    		try {
    			// 列举所有可用的串口
    	        SerialPort[] commPorts = SerialPort.getCommPorts();
    	        for (SerialPort port : commPorts) {
    	        	String des = port.getPortDescription();
    	        	System.out.println(des);
    	        	if(RString.isNotBlank(portDescription) &&  !portDescription.equals(des)) {
    	        		//端口描述不為空,需要检验 
    	        		continue;
    	        	}
    	        	
    	        	String portname = port.getSystemPortName();
    	        	if(RString.isNotBlank(systemPortName) &&  !systemPortName.equals(portname)) {
    	        		//端口不為空,需要检验 
    	        		continue;
    	        	}
    	        	return port;
    	        }
			} catch (Exception e) {
				// TODO: handle exception
			}
	        throw new BusinessException("获取设备异常,请重试");
    }
    
    public static void createListen(String portDescription,String systemPortName) {
    	if(scanSerialPort != null) {
			//如果是断线重连
    		scanSerialPort.closePort();
		}
    	//获取可用端口
    	scanSerialPort = connectPort(portDescription, systemPortName);
        try {
        	scanSerialPortName = scanSerialPort.getSystemPortName()+":"+scanSerialPort.getPortDescription();
        	scanSerialPort.addDataListener(new ScanDatLislen());
        	boolean isopen = scanSerialPort.openPort();
        	if(!isopen) {
        		//连接失败
        		 throw new BusinessException("连接设备失败,请重试,设备:"+portDescription+",端口"+systemPortName); 
        	}
        	System.out.println("open......");
        } catch (BusinessException e) {
        	throw e;
        }  catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//scanSerialPort.closePort();
		}
    }
}

##监听函数

package com.hzsmk.serial.service;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.hzsmk.common.util.RString;

import cn.hutool.core.swing.RobotUtil;

public class ScanDatLislen implements SerialPortDataListener{

	@Override
	public int getListeningEvents() {
		// TODO Auto-generated method stub
		return  SerialPort.LISTENING_EVENT_DATA_RECEIVED;
	}

	@Override
	public void serialEvent(SerialPortEvent event) {
		System.out.println("监听执行 LISTENING_EVENT_DATA_RECEIVED");
		// 读取数据
		try {
			byte[] buffer = event.getReceivedData(); // 缓冲区大小
	        if (buffer.length > 0) {
	            // 将读取的字节转换为字符串
	            String data = new String(buffer, 0, buffer.length, "UTF-8");
	            if(!data.startsWith("QRCODE") || !data.startsWith("SRC")) {
	                 String code = RString.byte2hex(buffer);
	                 if(code.startsWith("810169")) {
	                 	data = code;
	                 }
	            }
	            RobotUtil.keyPressString(data);
	        }
	        Thread.sleep(1000);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

}

核心逻辑,1、开启定时任务,定时检测设备是否在线

重点,重点,重点,重点,重点,重点,重点,重点,重点,重点,重点,重点,这里有一个比较恶心的API,SerialPort.isOpen() 这个API,在打开过端口之后,设备掉线或者插口拔掉后,它依然是open状态。 这里的OPEN时间上只能说是 本地开启的服务是否open ,并不能检测设备的在线状态。
所以实际上有用的 SerialPort.bytesAvailable() ,查看是否有byte数据可用。用这个检测设备是否在线

2、开启监听函数,可以监听端口或者数据接收的事件addDataListener

SerialPort.LISTENING_EVENT_DATA_RECEIVED; 有很多种状态,目前业务需求监听数据响应状态。

3、监听到数据后,可以把数据传输给第三方接口,或者保存下来,但是如果要和WEB界面交互,这里有一个小技巧,可以把数据张贴到键盘聚焦点,使用hutool工具RobotUtil.keyPressString(data); ,, 简单的说就是把串口通讯又转换为了 usb-kwb-hid通讯了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/602326.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

短视频矩阵系统源码saas开发--可视化剪辑、矩阵托管、多功能合一开发

短视频矩阵系统源码saas开发&#xff08;可视化剪辑、矩阵托管、智能私信聚合、线索转化、数据看板、seo关键词、子账号等多个板块开发&#xff09; 短视频矩阵系统是一种集成了多种功能的系统&#xff0c;旨在帮助用户在短视频平台上进行高效的内容创作、管理和发布。根据您提…

citylava:城市场景中VLMs的有效微调

citylava:城市场景中VLMs的有效微调 摘要IntroductionRelated WorkVision-Language ModelsVLMs in Driving Methodology CityLLaVA: Efficient Fine-Tuning for VLMs in City Scenario 摘要 在城市广阔且动态的场景中&#xff0c;交通安全描述与分析在从保险检查到事故预防的各…

Type C USB

参考内容地址 https://m.elecfans.com/article/2315364.html Type C是高速 USB3.0设备&#xff0c;不能有大电容在链路中。

【接地故障保护】剩余电流继电器及监控产品解决方案

安科瑞电气股份有限公司 祁洁 15000363176 一、产品型号 二、产品功能 1、对直接接触电击事故的防护 对直接接触电击事故的防护中&#xff0c;剩余电流继电器&#xff08;RCD&#xff09;只作为直接接触电击事故基本防护措施的补充保护措施&#xff08;不包括对相与相、相…

微信小程序音频怎么保存到手机

如果你想在微信小程序中收听美妙的音乐&#xff0c;又想将其保存到手机中&#xff0c;那么微信小程序音频怎么保存到手机这个问题就是你的最佳指南。 微信小程序音频下载工具我已经打包好了&#xff0c;有需要的自己下载一下 下载高手工具打包链接&#xff1a;百度网盘 请输入…

【Git】Git在Gitee上的基本操作指南

文章目录 1. 查看 git 版本2. 从Gitee克隆仓库&#xff1a;3. 复制文件到工作目录&#xff1a;4. 将未跟踪的文件添加到暂存区&#xff1a;5. 在本地提交更改&#xff1a;6. 将更改推送到远程仓库&#xff08;Gitee&#xff09;&#xff1a;7. Windows特定提示&#xff1a; 1. …

sh包装脚本

两个脚本,运行的时间间隔分别是一分钟和五分钟,放入到sh文件中,挂在后代,脚本里面的路径最好是绝对路径。 新建sh文件 新建 run_test.sh 文件,使其可以运行两个不同的 Python 脚本,一个每分钟运行一次,另一个每五分钟运行一次。下面是修改后的 run_test.sh 文件的示例:…

DOTA-Gly-Asp-Tyr-Met-Gly-Trp-Met-Asp-Phe-NH2,1306310-00-8,是一种重要的多肽化合物

一、试剂信息 名称&#xff1a;DOTA-Gly-Asp-Tyr-Met-Gly-Trp-Met-Asp-Phe-NH2CAS号&#xff1a;1306310-00-8结构式&#xff1a; 二、试剂内容 DOTA-Gly-Asp-Tyr-Met-Gly-Trp-Met-Asp-Phe-NH2是一种重要的多肽化合物&#xff0c;其CAS号为1306310-00-8。该多肽包含一个DO…

idea配置hive

idea配置hive 今天才知道&#xff0c;idea居然可以配置hive&#xff0c;步骤如下: view -> Tool Windows -> Database Database出来了之后&#xff0c;直接配置即可

STM32快速入门(定时器之输入捕获)

STM32快速入门&#xff08;定时器之输入捕获&#xff09; 前言 本节主要讲解STM32利用通用定时器&#xff0c;在输入引脚出现指定电平跳变时&#xff0c;将CNT的值锁存到CCR寄存器当中&#xff0c;从而计算PWM波形的频率、占空比、脉冲间隔、电平持续时间等。其功能的应用有&…

[C++基础编程]----预处理指令简介、typedef关键字和#define预处理指令之间的区别

目录 引言 正文 01-预处理指令简介 02-typedef关键字简介 03-#define预处理指令简介 04-#define预处理指令和typedef关键字的区别 &#xff08;1&#xff09;原理不同 &#xff08;2&#xff09;功能不同 &#xf…

【YOLOv8改进[Backbone]】使用SCINet改进YOLOv8在黑暗环境的目标检测效果

目录 一 SCINet 1 本文方法 ① 权重共享的照明学习 ② 自校准模块 ③ 无监督训练损失 二 使用SCINet助力YOLOv8在黑暗环境的目标检测效果 1 整体修改 2 配置文件 3 训练 其他 一 SCINet 官方论文地址&#xff1a;https://arxiv.org/pdf/2204.10137 官方代码地址&…

26_Scala集合常用API汇总

文章目录 1.mkString2.size&#xff0c;length&#xff0c;isEmpty,contains3.reverse ,length,distinct4.获取数据相关4.1数据准备4.2准确获取尾部last4.3 除了最后一个元素不要其他都要4.4从集合获取部分数据 5.删除数据5.1删除3个从左边5.2删除3个右边 6.切分数据splitAt(n:…

解决“您的连接不是私密连接”

目录 那么为什么会出现这样提示呢 https访问有什么不同 将http访问更改为https访问 当您在浏览网页时&#xff0c;遇到“您的连接不是私密连接”的提示&#xff0c;这通常表示浏览器认为您的连接不够安全。这是因为浏览器无法信任网站使用的SSL证书&#xff0c;或者网站没有…

【系统架构师】-UML-用例图(Use Case)

1、概述 用于表示系统功能需求&#xff0c;以及应用程序与用户或者与其他应用程序之间的交互关系。 2、组成 参与者&#xff08;Actors&#xff09;&#xff1a;与系统交互的用户或其他系统。用一个人形图标表示。用例&#xff08;Use Cases&#xff09;&#xff1a;系统需要…

平滑矢量图形综述:表示、创建、光栅化和图像矢量化的最新进展

摘要 平滑矢量图形领域探索了轻量级图像表示的表示、创建、光栅化和自动生成&#xff0c;常用于可缩放图像内容。过去几十年中&#xff0c;出现了多种概念性方法来表示具有平滑渐变的图像&#xff0c;每种方法都引发了独立的研究线索&#xff0c;包括流行的梯度网格和扩散曲线…

工程技术SCI期刊,中科院三区,一投就中,国人友刊!

一、期刊名称 Industrial Management & Data Systems 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;工程技术 影响因子&#xff1a;5.5 中科院分区&#xff1a;3区 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;$4120 三、期刊征稿范…

QT功能 实现静态内容国际化实验

文章目录 第一步&#xff1a;新建一个QT工程第二步&#xff1a;添加控件第三步&#xff1a;在pro文件中添加内容第四步&#xff1a;更新文件第五步&#xff1a;打开QT的Linguist第六步&#xff1a;添加翻译内容第七步&#xff1a;回到QT Creator中添加文件第八步&#xff1a;给…

Colibri for Mac v2.2.0激活版:专业级无损音乐播放器

Colibri for Mac是一款专为Mac用户设计的高分辨率无损音乐播放器。它基于BASS技术构建&#xff0c;为用户带来极致的音频体验。Colibri支持所有流行的无损和有损音频格式&#xff0c;如FLAC、MP3、AAC等&#xff0c;确保音乐播放的清晰度和完美度。其独特的清晰比特完美播放技术…

月均互动超50万!“仪式感”何以盛行?小红书数据分析揭示消费动力

现代年轻人&#xff1a;“赚不到钱&#xff0c;却爱花钱买仪式感”。审慎消费的当下&#xff0c;“仪式感消费”不减反增&#xff0c;小红书平台相关内容更是持续火爆&#xff0c;成为品牌圈粉消费者的关键词。 这届小红书用户&#xff0c;热衷为“仪式感”氪金 “XX的第一杯奶…
最新文章