Java 串口通信(RS232/485)
•
Jave
Java 串口通信(RS232/485)
- 一.串口通信页面
- 二.串口服务实现
-
- 1.Java 串口通信配置
-
- 1.扩展包和依赖库
- 2.Pom配置
- 2.启动类
- 3.工具包类
-
- 1.Common
- 2.Crc16Modbus
- 3.SerialUtil
- 4.WebSocket 配置
-
- 1.启动配置
- 2.监听配置
- 5.UI交互类
-
- 1.串口配置对象
- 2.串口信息获取接口
- 3.RS232接口
- 4.RS485接口
- 6.串口配置类
-
- 1.串口配置
- 2.RS232串口配置
- 3.RS232串口监听
- 4.RS485串口配置
- 5.RS485串口监听
- 三.UI代码
- 四.测试效果
-
- 1.串口通信
- 2.CRC16通信
一.串口通信页面

Java 实现串口通信,同时通过 WebSocket 与 UI 实时交互传递通信数据
准备工作:
虚拟串口工具:Launch Virtual Serial Port Driver
串口调试助手:SSCOM

RS485
根据 Modbus 协议,常规485通讯的信息发送形式如下: 地址 功能码 数据信息 校验码 1byte 1byte nbyte 2byte
在线 CRC检验码计算:CRC 测试链接
二.串口服务实现
1.Java 串口通信配置
1.扩展包和依赖库
RXTXcomm.jar 放入 {JAVA_HOME}/jre/lib/ext
rxtxserial.dll 放入 {JAVA_HOME}/jre/bin
以上两个包可以直接网上下载,注意和JDK版本搭配即可
2.Pom配置
串口通信包:rxtx
4.0.0 org.example SerialPort1.0-SNAPSHOT 8 8 UTF-8 org.springframework.boot spring-boot-starter-web2.7.4 org.projectlombok lombok1.18.24 org.springframework spring-websocket5.3.27 org.rxtx rxtx2.1.7 nexus-aliyun nexus-aliyun http://maven.aliyun.com/nexus/content/groups/public/ true false public aliyun nexus http://maven.aliyun.com/nexus/content/groups/public/ true false
2.启动类
package com.serial.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author
* @date 2023-07-01 12:41
* @since 1.8
*/
@SpringBootApplication
public class SerialApplication {
public static void main(String[] args) {
SpringApplication.run(SerialApplication.class,args);
}
}
3.工具包类
1.Common
package com.serial.demo.util;
/**
* @author
* @date 2023-07-03 22:17
* @since 1.8
*/
public class Common {
public static String HEX_STRING = "0123456789ABCDEF";
public static final String NONE = "无";
public static final String ODD = "奇";
public static final String EVEN = "偶";
public static final String FORMAT_HEX="HEX";
}
2.Crc16Modbus
CRC16 Modbus Java 实现:计算数据的校验码
package com.serial.demo.util;
/**
* @author
* @date 2023-07-04 20:37
* @since 1.8
*/
public class Crc16Modbus {
/**
* CRC 循环冗余校验 即通过生成多项式对原始数据进行计算,将计算结果拼接到数据上一起发送
* 接收方计算接收到的数据校验接收结果是否准确
* CRC 即对生成多项式的模二运算
*
* 1.预置1个16位的寄存器为十六进制 FFFF(即全为1),称此寄存器为CRC寄存器
* 2.把第1个8位二进制数据(帧头字节)与 CRC 寄存器的低8位相异或并写回寄存器 高8位数据不变
* 3.把 CRC 循环右移 高位补 0 取得移出位
* 4.如果移出位为 0 继续右移 如果移出位为 1 则 CRC 寄存器与多项式 A001(1010 0000 0000 0001)进行异或运算
* 5.重复步骤 3 和 4 直到右移 8 次
* 6.重复步骤 2 到 5 进行数据帧下一个字节的处理 直到将数据帧所有字节按上述步骤计算
* 7.根据需要将寄存器的高、低字节进行交换 得到最终 CRC码
*
*/
/**
* 初始值 CRC-16 寄存器
*/
private static final int INITIAL_VALUE = 0xFFFF;
private static final boolean IS_OUT_PUT_OVER_TURN = true;
/**
* 原始数据 + CRC码
*
* @param hexes 16 进制字符串
* @return
*/
public static byte[] getData(String... hexes) {
byte[] data = new byte[hexes.length];
int i = 0;
for (String hex:hexes){
//先转为数字在转为 byte
data[i++] = (byte) Integer.parseInt(hex, 16);
}
return merge(data);
}
/**
* 原始数据 + CRC码
*
* @param data
* @return
*/
public static byte[] merge(byte[] data) {
byte[] crc = getCrc16(data);
int dLen = data.length;
int cLen = crc.length;
byte[] result = new byte[dLen + cLen];
System.arraycopy(data,0,result,0,dLen);
System.arraycopy(crc,0,result,dLen,cLen);
return result;
}
/**
* 基于 CRC16 Modbus 计算校验码
* CRC 16 Modbus 默认多项式为 x16+x15+x2+1 => 8005 反转即 A001
*
* @param data
* @return
*/
private static byte[] getCrc16(byte[] data) {
int len = data.length;
int crc = INITIAL_VALUE;
int i, j;
for (i = 0; i < len; i++) {
// 把第一个 8 位二进制数据 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器
crc = ((crc & 0xFF00) | (crc & 0x00FF) ^ (data[i] & 0xFF));
for (j = 0; j < 8; j++) {
// 把 CRC 寄存器的内容右移一位(朝低位)用 0 填补最高位, 并检查右移后的移出位
if ((crc & 0x0001) > 0) {
// 如果移出位为 1, CRC寄存器与多项式A001进行异或
crc = crc >> 1;
crc = crc ^ 0xA001;
} else {
// 如果移出位为 0,再次右移一位
crc = crc >> 1;
}
}
}
return intToBytes(crc);
}
/**
* 将 int 转换成 byte 数组 低位在前 高位在后
*/
private static byte[] intToBytes(int value) {
byte[] src = new byte[2];
byte hig = (byte) ((value>>8) & 0xFF);
byte low = (byte) (value & 0xFF);
if (IS_OUT_PUT_OVER_TURN){
src[0] = low;
src[1] = hig;
} else {
src[0] = hig;
src[1] = low;
}
return src;
}
/**
* 将字节数组转换成十六进制字符串
*/
public static String byteTo16String(byte[] data) {
StringBuffer buffer = new StringBuffer();
for (byte b : data) {
byteToHex(buffer,b);
}
return buffer.toString().toUpperCase();
}
/**
* 将字节转换成十六进制字符串
*
* int 转 byte 对照表
* [128,255],0,[1,128)
* [-128,-1],0,[1,128)
*/
public static void byteToHex(StringBuffer buffer ,byte b) {
if (b < 0) {
buffer.append(Integer.toString(b + 256, 16));
} else if (b == 0) {
buffer.append("00 ");
} else if (b > 0 && b <= 15) {
buffer.append("0" + Integer.toString(b, 16));
} else if (b > 15) {
buffer.append(Integer.toString(b, 16));
}
buffer.append(" ");
}
}
3.SerialUtil
package com.serial.demo.util;
import gnu.io.SerialPort;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* @author
* @date 2023-07-03 21:52
* @since 1.8
*/
public class SerialUtil {
/**
* 转为 HEX
* @param str
* @return
*/
public static String toHex(String str){
StringBuffer sbf = new StringBuffer();
byte[] b = str.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sbf.append(hex.toUpperCase() + " ");
}
return sbf.toString().trim();
}
/**
*
* @param hex
* @return
*/
public static String toStr(String hex) {
return new String(hexToByte(hex));
}
/**
* 转 HEX 字节
* @param hex
* @return
*/
public static byte[] hexToByte(String hex){
hex = hex.toUpperCase().replace(" ","");
ByteArrayOutputStream bao = new ByteArrayOutputStream(hex.length() / 2);
// 将每2位16进制整数组装成一个字节
for (int i = 0; i < hex.length(); i += 2) {
bao.write((Common.HEX_STRING.indexOf(hex.charAt(i)) << 4 | Common.HEX_STRING.indexOf(hex.charAt(i + 1))));
}
return bao.toByteArray();
}
/**
* 获取校验位配置
* @param checkBit
* @return
*/
public static int getParity(String checkBit){
if (Common.NONE.equals(checkBit)){
return SerialPort.PARITY_NONE;
} else if (Common.ODD.equals(checkBit)){
return SerialPort.PARITY_ODD;
} else if (Common.EVEN.equals(checkBit)){
return SerialPort.PARITY_EVEN;
} else {
return SerialPort.PARITY_NONE;
}
}
/**
* 读取数据
* @param in
* @return
*/
public static byte[] readFromPort(InputStream in) {
byte[] bytes = {};
try {
// 缓冲区大小为一个字节
byte[] readBuffer = new byte[1];
int bytesNum = in.read(readBuffer);
while (bytesNum > 0) {
bytes = concat(bytes, readBuffer);
bytesNum = in.read(readBuffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
in = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
* 字节转换
* @param format
* @param b
* @return
*/
public static String printHexString(String format, byte[] b) {
String result = new String(b);
if (Common.FORMAT_HEX.equals(format)){
return SerialUtil.toHex(result);
}
return result;
}
/**
* 合并数组
*
* @param firstArray 第一个数组
* @param secondArray 第二个数组
* @return 合并后的数组
*/
public static byte[] concat(byte[] firstArray, byte[] secondArray) {
if (firstArray == null || secondArray == null) {
if (firstArray != null) {
return firstArray;
}
if (secondArray != null) {
return secondArray;
}
return null;
}
byte[] bytes = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
return bytes;
}
}
4.WebSocket 配置
1.启动配置
package com.serial.demo.socket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author
* @date 2023-07-02 21:05
* @since 1.8
*/
@Configuration
public class WebSocketConfig {
/**
* 开启 websocket 配置
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
2.监听配置
package com.serial.demo.socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author
* @date 2023-07-02 21:07
* @since 1.8
*/
@Slf4j
@Component
@ServerEndpoint("/websocket/{sid}")
public class SerialWebSocket {
/**
* 缓存通信实例
*/
private static Map webSocketMap = new ConcurrentHashMap<>(16);
/**
* 会话
*/
private Session session;
/**
* 标识
*/
private String sid;
/**
* 建立连接
* @param sid
* @param session
*/
@OnOpen
public void onOpen(@PathParam("sid") String sid,Session session){
this.session = session;
this.sid = sid;
webSocketMap.put(sid,this);
//sendMessage(sid,"Hello:");
}
/**
* 关闭连接
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid){
try {
SerialWebSocket socket = webSocketMap.remove(sid);
if (socket != null){
socket.session.close();
socket = null;
}
} catch (IOException e) {
log.error("Close {} exception:",sid,e);
}
}
/**
* 接收消息
* @param message
*/
@OnMessage
public void onMessage(String message){
log.info("sid {} msg {}",this.sid,message);
}
/**
* 发送消息
* @param message
* @param sid
*/
public static void sendMessage(String sid,String message){
SerialWebSocket socket = webSocketMap.get(sid);
if (socket != null){
try {
socket.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("Send {} message {} exception:",sid,message,e);
}
}
}
/**
* 广播消息
* @param message
*/
public static void broadcast(String message){
for (String sid:webSocketMap.keySet()){
sendMessage(sid,message);
}
}
}
5.UI交互类
1.串口配置对象
package com.serial.demo.entity;
import lombok.Data;
/**
* @author
* @date 2023-07-02 22:58
* @since 1.8
*/
@Data
public class SerialEntity {
private String portId;
private int bitRate;
private int dataBit;
private int stopBit;
private String checkBit;
private String format;
}
2.串口信息获取接口
package com.serial.demo.controller;
import com.serial.demo.config.SerialPortConfig;
import com.serial.demo.util.SerialUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author
* @date 2023-07-01 16:37
* @since 1.8
*/
@CrossOrigin
@RestController
@RequestMapping("/serial")
public class SerialController {
@Autowired
SerialPortConfig serial;
/**
* 获取端口列表
* @return
*/
@GetMapping("/getSerialPortList")
public List getSerialPortList(){
return serial.getSerialPortList();
}
/**
* 字符串 转 HEX
* @return
*/
@GetMapping("/toHex")
public String toHex(String str){
return SerialUtil.toHex(str);
}
/**
* HEX 转 字符串
* @return
*/
@GetMapping("/toStr")
public String toStr(String hex){
return SerialUtil.toStr(hex);
}
}
3.RS232接口
package com.serial.demo.controller;
import com.serial.demo.config.Rs232Config;
import com.serial.demo.entity.SerialEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author
* @date 2023-07-03 1:03
* @since 1.8
*/
@CrossOrigin
@RestController
@RequestMapping("/serial/232")
public class Rs232Controller {
@Autowired
Rs232Config rs232Config;
/**
* 监听端口
* @param serial
*/
@PostMapping("/open")
public boolean open(@RequestBody SerialEntity serial){
return rs232Config.openPort(serial);
}
/**
* 获取端口列表
* @return
*/
@GetMapping("/close/{portId}")
public void close(@PathVariable("portId") String portId){
rs232Config.closePort(portId);
}
/**
* 获取端口列表
* @return
*/
@GetMapping("/send/{portId}/{format}/{msg}")
public void close(@PathVariable("portId") String portId,@PathVariable("format") String format,@PathVariable("msg") String msg){
rs232Config.sendData(portId,format,msg);
}
}
4.RS485接口
package com.serial.demo.controller;
import com.serial.demo.config.Rs485Config;
import com.serial.demo.entity.SerialEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author
* @date 2023-07-03 23:33
* @since 1.8
*/
@CrossOrigin
@RestController
@RequestMapping("/serial/485")
public class Rs485Controller {
@Autowired
Rs485Config rs485Config;
/**
* 监听端口
* @param serial
*/
@PostMapping("/open")
public boolean open(@RequestBody SerialEntity serial){
return rs485Config.openPort(serial);
}
/**
* 获取端口列表
* @return
*/
@GetMapping("/close/{portId}")
public void close(@PathVariable("portId") String portId){
rs485Config.closePort(portId);
}
/**
* 获取端口列表
* @return
*/
@GetMapping("/send/{portId}/{format}/{msg}")
public void close(@PathVariable("portId") String portId,@PathVariable("format") String format,@PathVariable("msg") String msg){
rs485Config.sendData(portId,format,msg);
}
}
6.串口配置类
1.串口配置
package com.serial.demo.config;
import gnu.io.CommPortIdentifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author
* @date 2023-07-03 1:01
* @since 1.8
*/
@Slf4j
@Component
public class SerialPortConfig {
/**
* 缓存端口信息
*/
private static Map serialMap;
@PostConstruct
private void init(){
refreshCom();
}
/**
* 刷新端口
*/
public void refreshCom(){
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
CommPortIdentifier serial;
Map temp = new ConcurrentHashMap<>(16);
while (portList.hasMoreElements()){
serial = portList.nextElement();
if (serial.getPortType() == CommPortIdentifier.PORT_SERIAL){
temp.put(serial.getName(),serial);
}
}
serialMap = Collections.unmodifiableMap(temp);
}
/**
* 获取端口列表
* @return
*/
public List getSerialPortList(){
return serialMap.keySet().stream().sorted().collect(Collectors.toList());
}
/**
* 获取串口
* @return
*/
public Map getSerialMap(){
return serialMap;
}
}
2.RS232串口配置
package com.serial.demo.config;
import com.serial.demo.entity.SerialEntity;
import com.serial.demo.util.Common;
import com.serial.demo.util.SerialUtil;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author
* @date 2023-07-01 16:22
* @since 1.8
*/
@Slf4j
@Component
public class Rs232Config {
private static final int DELAY_TIME = 1000;
@Autowired
SerialPortConfig config;
/**
* 缓存端口实例
*/
private Map serialPortMap = new ConcurrentHashMap<>(16);
/**
* 监听端口
* @param serial
*/
public boolean openPort(SerialEntity serial) {
String portId = serial.getPortId();
CommPortIdentifier commPortIdentifier = config.getSerialMap().get(portId);
if (null != commPortIdentifier){
SerialPort serialPort = null;
int bitRate = 0,dataBit = 0,stopBit = 0,parity = 0;
try {
serialPort = (SerialPort) commPortIdentifier.open(portId,DELAY_TIME);
// 设置监听器生效 当有数据时通知
serialPort.notifyOnDataAvailable(true);
// 比特率、数据位、停止位、奇偶校验位
bitRate = serial.getBitRate();
dataBit = serial.getDataBit();
stopBit = serial.getStopBit();
parity = SerialUtil.getParity(serial.getCheckBit());
serialPort.setSerialPortParams(bitRate, dataBit, stopBit,parity);
} catch (PortInUseException e) {
log.error("Open CommPortIdentifier {} Exception:",serial.getPortId(),e );
return false;
} catch (UnsupportedCommOperationException e) {
log.error("Set SerialPortParams BitRate {} DataBit {} StopBit {} Parity {} Exception:",bitRate,dataBit,stopBit,parity,e);
return false;
}
// 设置当前串口的输入输出流
InputStream input;
OutputStream output;
try {
input = serialPort.getInputStream();
output = serialPort.getOutputStream();
} catch (IOException e) {
log.error("Get serialPort data stream exception:",e);
return false;
}
// 给当前串口添加一个监听器
try {
serialPort.addEventListener(new Serial232Listener(input,output,serial.getFormat()));
} catch (TooManyListenersException e) {
log.error("Get serialPort data stream exception:",e);
return false;
}
serialPortMap.put(portId,serialPort);
return true;
}
return false;
}
/**
* 关闭端口
* @param portId
*/
public void closePort(String portId){
SerialPort serialPort = serialPortMap.remove(portId);
if (null != serialPort){
serialPort.close();
}
}
/**
* 发送数据
* @param portId
* @param format
* @param message
*/
public void sendData(String portId,String format,String message){
SerialPort serialPort = serialPortMap.get(portId);
if (null == serialPort){
return;
}
OutputStream output = null;
try {
byte[] bytes;
if (Common.FORMAT_HEX.equals(format)){
bytes = SerialUtil.hexToByte(message);
} else {
bytes = message.getBytes(StandardCharsets.UTF_8);
}
output = serialPort.getOutputStream();
output.write(bytes);
output.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (null != output){
try {
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
3.RS232串口监听
package com.serial.demo.config;
import com.serial.demo.socket.SerialWebSocket;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author
* @date 2023-07-01 17:06
* @since 1.8
*/
public class Serial232Listener implements SerialPortEventListener {
InputStream inputStream;
OutputStream outputStream;
String format;
public Serial232Listener(InputStream input, OutputStream output, String format){
inputStream = input;
outputStream = output;
this.format = format;
}
@Override
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 = null;
int availableBytes = 0;
try {
availableBytes = inputStream.available();
while (availableBytes > 0) {
readBuffer = SerialUtil.readFromPort(inputStream);
String needData = Crc16Modbus.byteTo16String(readBuffer);
SerialWebSocket.broadcast(needData);
availableBytes = inputStream.available();
}
} catch (IOException e) {
}
default:
break;
}
}
}
4.RS485串口配置
package com.serial.demo.config;
import com.serial.demo.entity.SerialEntity;
import com.serial.demo.util.Common;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author
* @date 2023-07-03 1:00
* @since 1.8
*/
@Slf4j
@Component
public class Rs485Config {
private static final int DELAY_TIME = 1000;
@Autowired
SerialPortConfig config;
/**
* 缓存端口实例
*/
private Map serialPortMap = new ConcurrentHashMap<>(16);
/**
* 监听端口
* @param serial
*/
public boolean openPort(SerialEntity serial) {
String portId = serial.getPortId();
CommPortIdentifier commPortIdentifier = config.getSerialMap().get(portId);
if (null != commPortIdentifier){
SerialPort serialPort;
int bitRate = 0,dataBit = 0,stopBit = 0,parity = 0;
try {
serialPort = (SerialPort) commPortIdentifier.open(portId,DELAY_TIME);
// 设置监听器生效 当有数据时通知
serialPort.notifyOnDataAvailable(true);
serialPort.setDTR(true);
serialPort.setRTS(true);
// 比特率、数据位、停止位、奇偶校验位
bitRate = serial.getBitRate();
dataBit = serial.getDataBit();
stopBit = serial.getStopBit();
parity = SerialUtil.getParity(serial.getCheckBit());
serialPort.setSerialPortParams(bitRate, dataBit, stopBit,parity);
} catch (PortInUseException e) {
log.error("Open CommPortIdentifier {} Exception:",serial.getPortId(),e );
return false;
} catch (UnsupportedCommOperationException e) {
log.error("Set SerialPortParams BitRate {} DataBit {} StopBit {} Parity {} Exception:",bitRate,dataBit,stopBit,parity,e);
return false;
}
// 设置当前串口的输入输出流
InputStream input;
OutputStream output;
try {
input = serialPort.getInputStream();
output = serialPort.getOutputStream();
} catch (IOException e) {
log.error("Get serialPort data stream exception:",e);
return false;
}
// 给当前串口添加一个监听器
try {
serialPort.addEventListener(new Serial485Listener(input,output,serial.getFormat()));
} catch (TooManyListenersException e) {
log.error("Get serialPort data stream exception:",e);
return false;
}
serialPortMap.put(portId,serialPort);
return true;
}
return false;
}
/**
* 关闭端口
* @param portId
*/
public void closePort(String portId){
SerialPort serialPort = serialPortMap.remove(portId);
if (null != serialPort){
serialPort.close();
}
}
/**
* 发送数据
* @param portId
* @param format
* @param message
*/
public void sendData(String portId,String format,String message){
SerialPort serialPort = serialPortMap.get(portId);
if (null == serialPort){
return;
}
OutputStream output = null;
try {
byte[] bytes = new byte[0];
if (Common.FORMAT_HEX.equals(format)){
bytes = SerialUtil.hexToByte(message);
bytes = Crc16Modbus.merge(bytes);
}
output = serialPort.getOutputStream();
output.write(bytes);
output.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (null != output){
try {
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
5.RS485串口监听
package com.serial.demo.config;
import com.serial.demo.socket.SerialWebSocket;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author
* @date 2023-07-03 23:21
* @since 1.8
*/
public class Serial485Listener implements SerialPortEventListener {
InputStream inputStream;
OutputStream outputStream;
String format;
public Serial485Listener(InputStream input, OutputStream output, String format){
inputStream = input;
outputStream = output;
this.format = format;
}
@Override
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 = null;
int availableBytes = 0;
try {
availableBytes = inputStream.available();
while (availableBytes > 0) {
readBuffer = SerialUtil.readFromPort(inputStream);
String needData = printHexString(readBuffer);
SerialWebSocket.broadcast(needData);
availableBytes = inputStream.available();
}
} catch (IOException e) {
}
default:
break;
}
}
/**
* 转为 16 进制字符串
* @param b
* @return
*/
public static String printHexString(byte[] b) {
return Crc16Modbus.byteTo16String(b);
}
}
三.UI代码
Serial Communication
四.测试效果
1.串口通信
ASCII 收数

ASCII发数

切换为自动发送后即自动发送当前数据

Hex 收数

Hex 发数

2.CRC16通信
Hex 收数

Hex 发数

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/901621e1dc.html
