SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据

记录一下使用SpringBoot+jSerialComm实现Java串口通信,使用Java语言开发串口,对串口进行读写操作,在win和linux系统都是可以的,有一点好处是不需要导入额外的文件。

案例demo源码:SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据

之前使用RXTXcomm实现Java串口通信,这种方式对linux(centos)的支持效果不好还有些问题 但在win下面使用还不错,原文地址:SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据

不需要额外导入文件 比如dll 只需要导入对应的包

 <dependency><groupId>com.fazecast</groupId><artifactId>jSerialComm</artifactId><version>2.9.2</version></dependency>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><groupId>boot.example.jSerialComm</groupId><artifactId>boot-example-jSerialComm-2.0.5</artifactId><version>0.0.1-SNAPSHOT</version><name>boot-example-jSerialComm-2.0.5</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.fazecast</groupId><artifactId>jSerialComm</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>boot.example.SerialPortApplication</mainClass><includeSystemScope>true</includeSystemScope><!--外部进行打包--></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

SerialPortApplication启动类

package boot.example;import boot.example.serialport.SerialPortManager;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.annotation.PreDestroy;
import java.io.IOException;/***  蚂蚁舞*/
@SpringBootApplication
@EnableScheduling
@EnableAsync
public class SerialPortApplication implements CommandLineRunner {public static void main(String[] args) throws IOException {SpringApplication.run(SerialPortApplication.class, args);}@Overridepublic void run(String... args) throws Exception {try{//  winSerialPortManager.connectSerialPort("COM1");//  linux centos//SerialPortManager.connectSerialPort("ttyS1");} catch (Exception e){System.out.println(e.toString());}}@PreDestroypublic void destroy() {SerialPortManager.closeSerialPort();}}

SwaggerConfig

package boot.example;import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;/***  蚂蚁舞*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {@Beanpublic Docket createRestApi(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).paths(Predicates.not(PathSelectors.regex("/error.*"))).paths(PathSelectors.regex("/.*")).build().apiInfo(apiInfo());}private ApiInfo apiInfo(){return new ApiInfoBuilder().title("SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据").description("测试接口").version("0.01").build();}}

SerialPortController

package boot.example.controller;import boot.example.serialport.ConvertHexStrAndStrUtils;
import boot.example.serialport.SerialPortManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;/***  蚂蚁舞*/
@Controller
@RequestMapping("/serialPort")
public class SerialPortController {@GetMapping("/list")@ResponseBodypublic List<String> listPorts() {List<String> portList = SerialPortManager.getSerialPortList();if(!portList.isEmpty()){return portList;}return null;}@PostMapping("/send/{hexData}")@ResponseBodypublic String sendPorts(@PathVariable("hexData") String hexData) {if (SerialPortManager.SERIAL_PORT_STATE){SerialPortManager.sendSerialPortData(ConvertHexStrAndStrUtils.hexStrToBytes(hexData));return "success";}return "fail";}}

ConvertHexStrAndStrUtils

package boot.example.serialport;import java.nio.charset.StandardCharsets;/***  蚂蚁舞*/
public class ConvertHexStrAndStrUtils {private static final char[] HEXES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};public static String bytesToHexStr(byte[] bytes) {if (bytes == null || bytes.length == 0) {return null;}StringBuilder hex = new StringBuilder(bytes.length * 2);for (byte b : bytes) {hex.append(HEXES[(b >> 4) & 0x0F]);hex.append(HEXES[b & 0x0F]);}return hex.toString().toUpperCase();}public static byte[] hexStrToBytes(String hex) {if (hex == null || hex.length() == 0) {return null;}char[] hexChars = hex.toCharArray();byte[] bytes = new byte[hexChars.length / 2];   // 如果 hex 中的字符不是偶数个, 则忽略最后一个for (int i = 0; i < bytes.length; i++) {bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);}return bytes;}public static String strToHexStr(String str) {StringBuilder sb = new StringBuilder();byte[] bs = str.getBytes();int bit;for (int i = 0; i < bs.length; i++) {bit = (bs[i] & 0x0f0) >> 4;sb.append(HEXES[bit]);bit = bs[i] & 0x0f;sb.append(HEXES[bit]);}return sb.toString().trim();}public static String hexStrToStr(String hexStr) {//能被16整除,肯定可以被2整除byte[] array = new byte[hexStr.length() / 2];try {for (int i = 0; i < array.length; i++) {array[i] = (byte) (0xff & Integer.parseInt(hexStr.substring(i * 2, i * 2 + 2), 16));}hexStr = new String(array, StandardCharsets.UTF_8);} catch (Exception e) {e.printStackTrace();return "";}return hexStr;}}

SerialPortManager

package boot.example.serialport;import com.fazecast.jSerialComm.SerialPort;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/***  蚂蚁舞*/
public class SerialPortManager {public static final int SERIAL_BAUD_RATE = 115200;public static volatile boolean SERIAL_PORT_STATE = false;public static volatile SerialPort SERIAL_PORT_OBJECT = null;//查找所有可用端口public static List<String> getSerialPortList() {// 获得当前所有可用串口SerialPort[] serialPorts = SerialPort.getCommPorts();List<String> portNameList = new ArrayList<String>();// 将可用串口名添加到List并返回该Listfor(SerialPort serialPort:serialPorts) {System.out.println(serialPort.getSystemPortName());portNameList.add(serialPort.getSystemPortName());}//去重portNameList = portNameList.stream().distinct().collect(Collectors.toList());return portNameList;}//  连接串口public static void connectSerialPort(String portName){try {SerialPort serialPort = SerialPortManager.openSerialPort(portName, SERIAL_BAUD_RATE);TimeUnit.MILLISECONDS.sleep(2000);//给当前串口对象设置监听器serialPort.addDataListener(new SerialPortListener(new SerialPortCallback()));if(serialPort.isOpen()) {SERIAL_PORT_OBJECT = serialPort;SERIAL_PORT_STATE = true;System.out.println(portName+"-- start success");}} catch (InterruptedException ex) {ex.printStackTrace();}}//  打开串口public static SerialPort openSerialPort(String portName, Integer baudRate) {SerialPort serialPort = SerialPort.getCommPort(portName);if (baudRate != null) {serialPort.setBaudRate(baudRate);}if (!serialPort.isOpen()) {  //开启串口serialPort.openPort(1000);}else{return serialPort;}serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);serialPort.setComPortParameters(baudRate, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, 1000, 1000);return serialPort;}//  关闭串口public static void closeSerialPort() {if (SERIAL_PORT_OBJECT != null && SERIAL_PORT_OBJECT.isOpen()){SERIAL_PORT_OBJECT.closePort();SERIAL_PORT_STATE = false;SERIAL_PORT_OBJECT = null;}}//  发送字节数组public static void sendSerialPortData(byte[] content) {if (SERIAL_PORT_OBJECT != null && SERIAL_PORT_OBJECT.isOpen()){SERIAL_PORT_OBJECT.writeBytes(content, content.length);}}//  读取字节数组public static byte[] readSerialPortData() {if (SERIAL_PORT_OBJECT != null && SERIAL_PORT_OBJECT.isOpen()){byte[] reslutData = null;try {if (!SERIAL_PORT_OBJECT.isOpen()){return null;};int i=0;while (SERIAL_PORT_OBJECT.bytesAvailable() > 0 && i++ < 5) Thread.sleep(20);byte[] readBuffer = new byte[SERIAL_PORT_OBJECT.bytesAvailable()];int numRead = SERIAL_PORT_OBJECT.readBytes(readBuffer, readBuffer.length);if (numRead > 0) {reslutData = readBuffer;}} catch (InterruptedException e) {e.printStackTrace();}return reslutData;}return null;}}

SerialPortListener

package boot.example.serialport;import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;/***  蚂蚁舞*/
public class SerialPortListener implements SerialPortDataListener {private final SerialPortCallback serialPortCallback;public SerialPortListener(SerialPortCallback serialPortCallback) {this.serialPortCallback = serialPortCallback;}@Overridepublic int getListeningEvents() { //必须是return这个才会开启串口工具的监听return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;}@Overridepublic void serialEvent(SerialPortEvent serialPortEvent) {if (serialPortCallback != null) {serialPortCallback.dataAvailable();}}
}

SerialPortCallback

package boot.example.serialport;import java.text.SimpleDateFormat;
import java.util.Date;/***  蚂蚁舞*/
public class SerialPortCallback {public void dataAvailable() {try {//当前监听器监听到的串口返回数据 backbyte[] back = SerialPortManager.readSerialPortData();System.out.println("back-"+(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))+"--"+ConvertHexStrAndStrUtils.bytesToHexStr(back));String s = ConvertHexStrAndStrUtils.bytesToHexStr(back);System.out.println("rev--data:"+s);//throw new Exception();} catch (Exception e) {System.out.println(e.toString());}}
}

项目结构
myw

demo使用的波特率是115200 其他参数就默认的就好,一般只有波特率改动

启动项目和启动com工具(项目和com之间使用的是com1和com2虚拟串口 虚拟串口有工具的,比如Configure Virtual Serial Port Driver)
myw
可以看到com1和com2都已经在使用 应用程序用的com1端口 com工具用的com2端口,这样的虚拟串口工具可以模拟调试使用的 应用程序通过com1向com2发送数据 ,com工具通过com2向com1的应用程序发送数据,全双工双向的,如此可以测试了。

访问地址

http://localhost:24810/doc.html

查看当前的串口 win系统下的两个虚拟串口
myw
通过虚拟串口发送数据到com工具
myw
通过com工具查看收到的数据已经发送数据给应用程序
myw
控制台收到数据
myw
记录着,将来用得着。

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

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

相关文章

【已更新代码图表】2023数学建模国赛E题python代码--黄河水沙监测数据分析

E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c;以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位于小浪底水…

【算法题】小红书2023秋招提前批算法真题解析

文章目录 题目来源T1&#xff1a;5900: 【DP】小红书2023秋招提前批-连续子数组最大和5801: 【二分查找】小红书2023秋招提前批-精华帖子解法1——排序滑动窗口解法2——前缀和 二分查找 5000: 【模拟】小红书2023秋招提前批-小红的数组构造解法——数学 5300: 【哈希表】小红…

(高阶)Redis 7 第10讲 单线程 与 多线程 入门篇

面试题 1.Redis 是单线程还是多线程 最早的版本3.x是单线程。 版本4.x,严格意义不是单线程。负责处理客户端请求的线程是单线程,开始加入异步删除。 6.0.x版本后明确使用全新的多线程来解决问题 2.说说IO多路复用3.Redis 为什么快IO多路复用+epoll函…

高频golang面试题:简单聊聊内存逃逸?

文章目录 问题怎么答举例 问题 知道golang的内存逃逸吗&#xff1f;什么情况下会发生内存逃逸&#xff1f; 怎么答 golang程序变量会携带有一组校验数据&#xff0c;用来证明它的整个生命周期是否在运行时完全可知。如果变量通过了这些校验&#xff0c;它就可以在栈上分配。…

【力扣】96. 不同的二叉搜索树 <动态规划>

【力扣】96. 不同的二叉搜索树 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5 示例 2&#xff1a; 输入&am…

代码随想录算法训练营第38天 | ● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

文章目录 前言一、理论基础二、509. 斐波那契数三、70. 爬楼梯四、746. 使用最小花费爬楼梯总结 前言 动态规划 一、理论基础 1.基础 2.背包问题 3.打家劫舍 4.股票问题 5.子序列问题 动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;…

【美团3.18校招真题1】

大厂笔试真题网址&#xff1a;https://codefun2000.com/ 塔子哥刷题网站博客&#xff1a;https://blog.codefun2000.com/ 小美剪彩带 提交网址&#xff1a;https://codefun2000.com/p/P1088 题意&#xff1a;找出区间内不超过k种数字子数组的最大长度 使用双指针的方式&…

小程序的使用

微信小程序开发 外部链接别人的总结查看&#xff08;超详细保姆式教程&#xff09; 基础语法 1.数据绑定 1.1 初始化数据 页面.js的data选项中Page({data: {motto: Hello World,id:18} })使用数据 单项数据流&#xff1a;Mustache 语法 a)模板结构中使用双大括号 {{data}} …

安装程序报错“E: Sub-process /usr/bin/dpkg returned an error code (1)”的解决办法

今天在终端使用命令安装程序时出现了如下的报错信息。 E: Sub-process /usr/bin/dpkg returned an error code (1) 这种情况下安装什么程序最终都会报这个错&#xff0c;具体的报错截图如下图所示。 要解决这个问题&#xff0c;首先使用下面的命令进到相应的目录下。 cd /var/…

Java“牵手”唯品会商品列表数据,关键词搜索唯品会商品数据接口,唯品会API申请指南

唯品会商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取唯品会商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问唯品会商城的网页来获取商品详情信息。以下是两种常用方法的介…

IDEA快捷键第二版

1、选择当前行和上一行 按住 Shift键 再按两下向上键&#xff08; ↑ &#xff09;&#xff0c;按两下选两行&#xff0c;以此类推 2、将整个方法上移动 文本光标应放在方法的标头处&#xff0c;按住Ctrl Shift 向上键&#xff08; ↑ &#xff09;&#xff0c; 3、解包 …

无涯教程-JavaScript - DELTA函数

描述 DELTA函数测试两个值是否相等。如果number1 number2,则返回1&#xff1b;否则返回1。否则返回0。 您可以使用此功能来过滤一组值。如,通过合计几个DELTA函数,您可以计算相等对的计数。此功能也称为Kronecker Delta功能。 语法 DELTA (number1, [number2])争论 Argum…

Postman接口测试之Mock快速入门

一、Mock简介 1.Mock定义 Mock是一种比较特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行接口或单元测试。通常情况下&#xff0c;Mock与其他方法的区别是&#xff0c;用于模拟代码依赖对象&#xff0c;并允许设置对应的期望值。简单一点来讲&#xff0c;就是Mock创建…

CSS 滚动驱动动画 scroll()

CSS 滚动驱动动画 scroll() animation-timeline 通过 scroll() 指定可滚动元素与滚动轴来为容器动画提供一个匿名的 scroll progress timeline. 通过元素在顶部和底部(或左边和右边)的滚动推进 scroll progress timeline. 并且元素滚动的位置会被转换为百分比, 滚动开始被转化为…

Vue3中快速简单使用CKEditor 5富文本编辑器

Vue3简单使用CKEditor 5 前言准备定制基础配置富文本配置目录当前文章demo目录结构 快速使用demo 前言 CKEditor 5就是内嵌在网页中的一个富文本编辑器工具 CKEditor 5开发文档&#xff08;英文&#xff09;&#xff1a;https://ckeditor.com/docs/ckeditor5/latest/index.htm…

SpringMVC:从入门到精通,7篇系列篇带你全面掌握--三.使用SpringMVC完成增删改查

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringMVC的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 效果演示 一.导入项目的相关依赖 二.…

港联证券:大数据看北上资金胜率:整体跑赢市场,六成持股浮亏

一直以来&#xff0c;北上资金被称为“聪明资金”&#xff0c;其一举一动备受出资者重视。盛名之下&#xff0c;其战绩究竟如何&#xff1f;复盘前史数据发现&#xff0c;北上资金全体业绩跑赢沪深300指数&#xff0c;但现在持仓个股浮亏占比约六成。 8月&#xff0c;北上资金…

npm publish包报404,is not in the npm registry错误

1. 指定发布目标2. 登录npm&#xff0c;使用登录名发布包&#xff0c;包名命名原则“登录名/包名”&#xff0c;或 “包名” 3. 删除某一个版本npm unpublish pvfhv/eslint-config-prettier1.0.1 --force 删除后的版本不能重复使用&#xff0c;正式解释&#xff1a; Unfortun…

无涯教程-JavaScript - IMLOG2函数

描述 IMLOG2函数以x yi或x yj文本格式返回复数的以2为底的对数。可以从自然对数计算复数的以2为底的对数,如下所示- $$\log_2(x yi)(log_2e)\ln(x yi)$$ 语法 IMLOG2 (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the bas…

【web开发】4.JavaScript与jQuery

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、JavaScript与jQuery二、JavaScript常用的基本功能1.插入位置2.注释3.变量4.数组5.滚动字符 三、jQuery常用的基本功能1.引入jQuery2.寻找标签3.val、text、appe…