java-串口通讯-连接硬件

news2024/11/26 0:56:21

串口通信(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.coloradmin.cn/o/1651153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

04.2.配置应用集

配置应用集 应用集的意思就是&#xff1a;将多个监控项添加到一个应用集里面便于管理。 创建应用集 填写名称并添加 在监控项里面找到对应的自定义监控项更新到应用集里面 选择对应的监控项于应用集

45 套接字

本节重点 认识ip地址&#xff0c;端口号&#xff0c;网络字节序等网络编程中的基本概念 学习scoket&#xff0c;api的基本用法 能够实现一个简单的udp客户端/服务端 能够实现一个简单的tcp客户端/服务器&#xff08;但链接版本&#xff0c;多进程版本&#xff0c;多线程版本&a…

时间复杂度与空间复杂度(上篇)

目录 前言时间复杂度 前言 算法在运行的过程中要消耗时间资源和空间资源 所以衡量一个算法的好坏要看空间复杂度和时间复杂度&#xff0c; 时间复杂度衡量一个算法的运行快慢 空间复杂度是一个算法运行所需要的额外的空间 一个算法中我们更关心的是时间复杂度 时间复杂度 时…

【快捷部署】023_HBase(2.3.6)

&#x1f4e3;【快捷部署系列】023期信息 编号选型版本操作系统部署形式部署模式复检时间023HBase2.3.6Ubuntu 20.04tar包单机2024-05-07 注意&#xff1a;本脚本非全自动化脚本&#xff0c;有2次人工干预&#xff0c;第一次是确认内网IP&#xff0c;如正确直接回车即可&#…

什么软件能在桌面提醒我 电脑桌面提醒软件

在这个信息爆炸的时代&#xff0c;我们每个人每天都需要处理海量的信息和任务。有时候&#xff0c;即便是再细心的人&#xff0c;也难免会因为事情太多而忘记一些重要的细节。 我就经常遇到这样的问题&#xff0c;明明记得自己有个重要的会议要参加&#xff0c;或者有个关键的…

扭蛋机小程序在互联网浪潮中的崛起与发展

随着互联网的快速发展&#xff0c;各种线上娱乐方式层出不穷&#xff0c;其中扭蛋机小程序凭借其独特的魅力&#xff0c;在互联网浪潮中迅速崛起并发展壮大。扭蛋机小程序不仅打破了传统扭蛋机的地域限制和操作不便&#xff0c;还融入了丰富的互动元素和便捷性&#xff0c;满足…

纯血鸿蒙APP实战开发——自定义安全键盘案例

介绍 金融类应用在密码输入时&#xff0c;一般会使用自定义安全键盘。本示例介绍如何使用TextInput组件实现自定义安全键盘场景&#xff0c;主要包括TextInput.customKeyboard绑定自定义键盘、自定义键盘布局和状态更新等知识点。 效果图预览 实现思路 1. 使用TextInput的cu…

为什么你的企业需要微信小程序?制作微信小程序有什么好处?

什么是小程序&#xff1f; WeChat小程序作为更大的WeChat生态系统中的子应用程序。它们就像更小、更基本的应用程序&#xff0c;在更大的应用程序&#xff08;WeChat&#xff09;中运行。这些程序为用户提供了额外的高级功能&#xff0c;以便在使用WeChat服务时加以利用。根据…

linux系统 虚拟机的安装详细步骤

window&#xff1a; (1) 个人&#xff1a;win7 win10 win11 winxp (2)服务器&#xff1a;windows server2003 2008 2013 linux&#xff1a; (1)centos7 5 6 8 (2)redhat (3)ubuntu (4)kali 什么是linux: 主要是基于命令来完成各种操作&#xff0c;类似于DO…

0基础学PHP有多难?

php作为web端最佳的开发语言&#xff0c;没有华而不实&#xff0c;而是经受住了时间考验&#xff0c;是一门非常值得学习的编程语言。 目前市场上各种网站、管理系统、小程序、APP等&#xff0c;基本都是使用PHP开发的&#xff0c;也侧面反映了PHP的需求以及学习的必要性&…

UTONMOS:真正的“游戏元宇宙”还有多遥远?

元宇宙来源于科幻小说的概念&#xff0c;已成为真实世界中的流行语。围绕这一新兴概念&#xff0c;一场产、学、研的实践正在展开。 数字化转型中&#xff0c;元宇宙能否担当大任&#xff1f;这些新概念在中国语境下如何落地&#xff1f;本文将深入挖掘国内元宇宙游戏产业的发…

数据结构-线性表-应用题-2.2-6

从有序顺序表中删除所有其值重复的元素&#xff0c;使表中的元素的值均不同 有序顺序表&#xff0c;值相同的元素一定在连续的位置上&#xff0c;初始时将第一个元素是为非重复的有序表&#xff0c;之后依次判断后面的元素是否与前面的非重复表的最后一个元素相同&#xff0c;…

当AI遇见现实:数智化时代的人类社会新图景

文章目录 一、数智化时代的机遇二、数智化时代的挑战三、如何适应数智化时代《图解数据智能》内容简介作者简介精彩书评目录精彩书摘强化学习什么是强化学习强化学习与监督学习的区别强化学习与无监督学习的区别 前言/序言 随着科技的日新月异&#xff0c;我们步入了一个前所未…

大数据硬核技能进阶:Spark3实战智能物业运营系统

Spark Streaming 是 Spark 的一个子模块&#xff0c;用于快速构建可扩展&#xff0c;高吞吐量&#xff0c;高容错的流处理程序。具有以下特点&#xff1a; 大数据硬核技能进阶&#xff1a;Spark3实战智能物业运营系统(网盘超清) 通过高级 API 构建应用程序&#xff0c;简单易…

【详细介绍下图搜索算法】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

技术分享-上海泗博MPI转以太网模块MPI-131实现Node-RED直接访问西门子PLC数据

上海泗博自动化MPI-131是一款用于西门子S7系列PLC&#xff08;包括S7-200、S7-300、S7-400&#xff09;以及西门子数控机床&#xff08;如840D、840DSL等&#xff09;的以太网通讯模块&#xff0c;无需编程&#xff0c;即插即用&#xff0c;支持通过模块上下载PLC程序和数据监控…

翻译《The Old New Thing》 - What is the deal with the ES_OEMCONVERT flag?

What is the deal with the ES_OEMCONVERT flag? - The Old New Thinghttps://devblogs.microsoft.com/oldnewthing/20050719-12/?p34893 Raymond Chen 在 2005年07月19日 ES_OEMCONVERT 标志是怎么回事&#xff1f; 简要 文章讨论了 ES_OEMCONVERT 编辑控件风格的起源和用途…

DNS域名解析服务的部署及优化方案

实验要求: 1.配置2台服务器要求如下&#xff1a; a&#xff09;服务器1&#xff1a; 主机名&#xff1a;dns-master.timinglee.org ip地址&#xff1a; 172.25.254.100 配置好软件仓库 b&#xff09;服务器2&#xff1a; 主机名&#xff1a;dns-slave.timinglee.org ip地址&am…

element-plus el-time-picker 时间段选择(可多选)

实现一个如图的时间段选择器 处理好时间回显逻辑&#xff0c;组件内[‘’,‘’],后端数据[{startTime:‘’,endTime:‘’}]处理好加和减的显示逻辑 <template><div><div v-for"(item, index) in currentChoose" :key"index" class"fl…

Linux实操之常用指令详解

文章目录 vi 和 vimvi 和 vim 基本使用 开机、重启和用户登录注销关机&重启命令用户登录和注销 用户管理基本介绍基本语法细节说明修改密码删除用户查询用户信息指令切换用户查看当前用户/登录用户用户组 实用指令指定运行级别帮助指令文件目录类时间和日期类搜索查找类压缩…