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

news2025/1/22 12:53:29

记录一下使用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);
    }

    @Override
    public void run(String... args) throws Exception {
        try{
            //  win
            SerialPortManager.connectSerialPort("COM1");
            //  linux centos
            //SerialPortManager.connectSerialPort("ttyS1");
        } catch (Exception e){
            System.out.println(e.toString());
        }

    }

    @PreDestroy
    public 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 {

    @Bean
    public 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")
    @ResponseBody
    public List<String> listPorts() {
        List<String> portList = SerialPortManager.getSerialPortList();
        if(!portList.isEmpty()){
            return portList;
        }
        return null;
    }


    @PostMapping("/send/{hexData}")
    @ResponseBody
    public 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并返回该List
        for(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;
    }

    @Override
    public int getListeningEvents() { //必须是return这个才会开启串口工具的监听
        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
    }

    @Override
    public 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 {
            //当前监听器监听到的串口返回数据 back
            byte[] 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.coloradmin.cn/o/991006.html

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

相关文章

easypoi导入数值精度丢失

记录一下easypoi导入数值,精度丢失的解决方案 1.导入的excel字段如图 2.easypoi解析CellValueService部分源码: 这个方法拿到的原始数据如图&#xff1a; 解决方法&#xff1a; 1.统一处理方式&#xff1a;在解析的时候使用DecimaFormat进行数据格式化 //格式化为6为小数 De…

Day57|leetcode 647. 回文子串、516.最长回文子序列

leetcode 647. 回文子串 题目链接&#xff1a;647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划&#xff0c;字符串性质决定了DP数组的定义 | LeetCode&#xff1a;647.回文子串_哔哩哔哩_bilibili 题目概述 给你一个字符串 s &#xff0c;…

使用动态住宅代理还能带来哪些好处?

一、什么是动态住宅代理ip 动态住宅代理是一种代理技术&#xff0c;它利用代理服务器中转用户和目标服务器之间的网络流量&#xff0c;实现用户真实位置的屏蔽。代理提供商会有自己的ip大池子&#xff0c;当你通过代理服务器向网站发送请求时&#xff0c;服务器会从池子中选中…

Spring系列文章:Spring6集成MyBatis3.5

1、引入依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version></dependency><dependency><groupId>org.mybatis</groupId><artif…

jsp页面出现“String cannot be resolved to a type”错误解决办法

篇首语&#xff1a;小编为大家整理&#xff0c;主要介绍了jsp页面出现“String cannot be resolved to a type”错误解决办法相关的知识&#xff0c;希望对你有一定的参考价值。 jsp页面出现“String cannot be resolved to a type”错误解决办法 解决办法&#xff1a; 右键项目…

软考高级架构师下篇-14面向服务架构设计理论

目录 1. 引言2. SOA的相关概念3. SOA的发展历史4. SOA的参考架构5. SOA 主要协议和规范6. SOA设计的标准要求7. SOA的作用与设计原则8. SOA的设计模式9. SOA构建与实施10. 前文回顾1. 引言 在面向服务的体系结构(Service-Oriented Architecture,SOA)中,服务的概念有了延伸…

详解:API开发【电商API封装商品数据SKU接口的开发接入】

电商API开发8.1 RESTful API的设计8.2 API的路由和控制器8.3 API的认证和授权 RESTful API的设计 RESTful API是一种通过HTTP协议发送和接收数据的API设计风格。它基于一些简单的原则&#xff0c;如使用HTTP动词来操作资源、使用URI来标识资源、使用HTTP状态码来表示操作结果等…

Java多线程(四)锁策略(CAS,死锁)和多线程对集合类的使用

锁策略&#xff08;CAS&#xff0c;死锁&#xff09;和多线程对集合类的使用 锁策略 1.乐观锁VS悲观锁 2.轻量级锁VS重量级锁 3.自旋锁VS挂起等待锁 4.互斥锁VS读写锁 5.可重入锁vs不可重入锁 死锁的第一种情况 死锁的第二种情况 死锁的第三种情况 CAS 1.实现原子类 …

苹果发布会:iPhone15系列

苹果将在北京时间9月13日凌晨1点召开发布会&#xff0c;本次发布会的主角是iPhone 15系列&#xff0c;包含四款机型&#xff1a;iPhone 15、iPhone 15 Plus、iPhone 15 Pro 以及 iPhone 15 Pro Max&#xff0c;本次发布会快科技全程视频直播&#xff0c;有关产品的细节也会在新…

四川百幕晟科技:抖音新店怎么快速起店?

抖音作为全球最大的短视频平台&#xff0c;拥有庞大的用户基础和强大的影响力&#xff0c;成为众多商家宣传产品、增加销量的理想选择。那么&#xff0c;如何快速开店并成功运营呢&#xff1f;下面描述了一些关键步骤。 1、如何快速开新店&#xff1f; 1、确定产品定位&#x…

系列一、前言

本系列文章是参考B站尚硅谷老师讲的 "尚硅谷Nginx教程由浅入深&#xff08;一套打通丨初学者也可掌握&#xff09;"系列课程&#xff0c;然后结合自己真实的操作而总结的系列文章。我也把自己学习、实操过程中的详细笔记以脑图的形式分享了出去&#xff0c;发现大家对…

SpringMVC实战crud增删改查

一.公共页面的跳转 1.编写页面跳转控制类 package com.YU.web;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;/*** author YU* create …

Apache Linki 1.3.1+DataSphereStudio+正常启动+微服务+端口号

我使用的是一键部署容器化版本&#xff0c;官方文章 默认会启动6个 Linkis 微服务&#xff0c;其中下图linkis-cg-engineconn服务为运行任务才会启动,一共七个 LINKIS-CG-ENGINECONN:38681 LINKIS-CG-ENGINECONNMANAGER:9102 引擎管理服务 LINKIS-CG-ENTRANCE:9104 计算治理入…

Linux 中的 chpasswd 命令及示例

chpasswd命令用于更改密码,尽管passwd命令也可以执行相同的操作。但它一次更改一个用户的密码,因此对于多个用户,使用chpasswd 。下图显示了passwd命令的使用。使用passwd我们正在更改来宾用户的密码。首先,您必须输入当前签名用户的密码,然后更改任何其他用户的密码。必须…

文本识别 (OCR)引擎之Tesseract的使用

Tesseract OCR Tesseract概述常见OCR识别平台下载安装配置命令使用语法测试验证 Tesseract的使用安装python库基本使用可能的异常更换语言字体库识别 Tesseract的训练 Tesseract 概述 Tesseract是一个开源文本识别 (OCR)引擎&#xff0c;是目前公认最优秀、最精确的开源OCR系统…

微服务井喷时代,我们如何规模化运维?

随着云原生技术发展及相关技术被越来越多运用到公司生产实践当中&#xff0c;有两种不可逆转的趋势&#xff1a; 1、微服务数量越来越多。原来巨型单体服务不断被拆解成一个个微服务&#xff0c;在方便功能复用及高效迭代的同时&#xff0c;也给运维带来了不少挑战&#xff1a;…

WorkPlus AI助理,基于ChatGPT的企业级知识问答机器人

随着人工智能技术的发展&#xff0c;WorkPlus AI助理以ChatGPT对话能力为基础&#xff0c;将企业数据与人工智能相结合&#xff0c;推出了面向企业的知识问答机器人。这一创新性的解决方案帮助企业高效管理和利用自身的知识资产&#xff0c;助力企业级人工智能的构建。与传统的…

React 入门实例教程

目录 一、HTML 模板 二、ReactDOM.render() 三、JSX 语法 四、组件 五、this.props.children 六、PropTypes 七、获取真实的DOM节点 八、this.state 九、表单 十、组件的生命周期 constructor() componentWillMount() render() componentDidMount() 组件生命周期…

MOV导出序列帧并在Unity中播放

MOV导出序列帧并在Unity中播放 前言项目将MOV变成序列帧使用TexturePacker打成一个图集将Json格式精灵表转换为tpsheet格式精灵表导入Unity并播放总结 鸣谢 前言 收集到一批还不错的MG动画&#xff0c;想要在Unity中当特效播放出来&#xff0c;那首先就得把MOV变成序列帧&…

Say0l的安全开发-弱口令扫描工具-My-crack【红队工具】

写在前面 终于终于&#xff0c;安全开发也练习一年半了&#xff0c;有时间完善一下项目&#xff0c;写写中间踩过的坑。 安全开发的系列全部都会上传至github&#xff0c;欢迎使用和star。 工具链接地址 https://github.com/SAY0l/my-crack 预览 My-Crack 工具介绍 更适合…